"Fossies" - the Fresh Open Source Software Archive

Member "cfengine-3.15.4/cf-agent/cf-agent.c" (7 Jun 2021, 70302 Bytes) of package /linux/misc/cfengine-3.15.4.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "cf-agent.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 3.15.3_vs_3.15.4.

    1 /*
    2   Copyright 2019 Northern.tech AS
    3 
    4   This file is part of CFEngine 3 - written and maintained by Northern.tech AS.
    5 
    6   This program is free software; you can redistribute it and/or modify it
    7   under the terms of the GNU General Public License as published by the
    8   Free Software Foundation; version 3.
    9 
   10   This program is distributed in the hope that it will be useful,
   11   but WITHOUT ANY WARRANTY; without even the implied warranty of
   12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   13   GNU General Public License for more details.
   14 
   15   You should have received a copy of the GNU General Public License
   16   along with this program; if not, write to the Free Software
   17   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
   18 
   19   To the extent this program is licensed as part of the Enterprise
   20   versions of CFEngine, the applicable Commercial Open Source License
   21   (COSL) may apply to this file if you as a licensee so wish it. See
   22   included file COSL.txt.
   23 */
   24 
   25 
   26 #include <platform.h>
   27 #include <generic_agent.h>
   28 
   29 #include <actuator.h>
   30 #include <audit.h>
   31 #include <cleanup.h>
   32 #include <eval_context.h>
   33 #include <verify_classes.h>
   34 #include <verify_databases.h>
   35 #include <verify_environments.h>
   36 #include <verify_exec.h>
   37 #include <verify_methods.h>
   38 #include <verify_processes.h>
   39 #include <verify_packages.h>
   40 #include <verify_users.h>
   41 #include <verify_services.h>
   42 #include <verify_storage.h>
   43 #include <verify_files.h>
   44 #include <verify_files_utils.h>
   45 #include <verify_vars.h>
   46 #include <addr_lib.h>
   47 #include <files_names.h>
   48 #include <files_interfaces.h>
   49 #include <files_repository.h>
   50 #include <files_edit.h>
   51 #include <files_properties.h>
   52 #include <item_lib.h>
   53 #include <vars.h>
   54 #include <conversion.h>
   55 #include <expand.h>
   56 #include <locks.h>
   57 #include <scope.h>
   58 #include <matching.h>
   59 #include <match_scope.h>
   60 #include <instrumentation.h>
   61 #include <promises.h>
   62 #include <unix.h>
   63 #include <attributes.h>
   64 #include <communication.h>
   65 #include <signals.h>
   66 #include <nfs.h>
   67 #include <processes_select.h>
   68 #include <list.h>
   69 #include <fncall.h>
   70 #include <rlist.h>
   71 #include <agent-diagnostics.h>
   72 #include <known_dirs.h>
   73 #include <cf-agent-enterprise-stubs.h>
   74 #include <syslog_client.h>
   75 #include <man.h>
   76 #include <bootstrap.h>
   77 #include <policy_server.h>
   78 #include <misc_lib.h>
   79 #include <buffer.h>
   80 #include <loading.h>
   81 #include <conn_cache.h>                 /* ConnCache_Init,ConnCache_Destroy */
   82 #include <net.h>
   83 #include <package_module.h>
   84 #include <string_lib.h>
   85 #include <cfnet.h>
   86 #include <repair.h>
   87 #include <dbm_api.h>                    /* CheckDBRepairFlagFile() */
   88 #include <sys/types.h>                  /* checking umask on writing setxid log */
   89 #include <sys/stat.h>                   /* checking umask on writing setxid log */
   90 
   91 #include <mod_common.h>
   92 
   93 #ifdef HAVE_AVAHI_CLIENT_CLIENT_H
   94 #ifdef HAVE_AVAHI_COMMON_ADDRESS_H
   95 #include <findhub.h>
   96 #endif
   97 #endif
   98 
   99 #include <ornaments.h>
  100 
  101 
  102 extern int PR_KEPT;
  103 extern int PR_REPAIRED;
  104 extern int PR_NOTKEPT;
  105 
  106 static bool ALLCLASSESREPORT = false; /* GLOBAL_P */
  107 static bool ALWAYS_VALIDATE = false; /* GLOBAL_P */
  108 static bool CFPARANOID = false; /* GLOBAL_P */
  109 static bool PERFORM_DB_CHECK = false;
  110 
  111 static const Rlist *ACCESSLIST = NULL; /* GLOBAL_P */
  112 
  113 static int CFA_BACKGROUND = 0; /* GLOBAL_X */
  114 static int CFA_BACKGROUND_LIMIT = 1; /* GLOBAL_P */
  115 
  116 static Item *PROCESSREFRESH = NULL; /* GLOBAL_P */
  117 
  118 static const char *const AGENT_TYPESEQUENCE[] =
  119 {
  120     "meta",
  121     "vars",
  122     "defaults",
  123     "classes",                  /* Maelstrom order 2 */
  124     "users",
  125     "files",
  126     "packages",
  127     "guest_environments",
  128     "methods",
  129     "processes",
  130     "services",
  131     "commands",
  132     "storage",
  133     "databases",
  134     "reports",
  135     NULL
  136 };
  137 
  138 /*******************************************************************/
  139 /* Agent specific variables                                        */
  140 /*******************************************************************/
  141 
  142 static void ThisAgentInit(void);
  143 static GenericAgentConfig *CheckOpts(int argc, char **argv);
  144 static char **TranslateOldBootstrapOptionsSeparate(int *argc_new, char **argv);
  145 static char **TranslateOldBootstrapOptionsConcatenated(int argc, char **argv);
  146 static void FreeFixedStringArray(int size, char **array);
  147 static void CheckAgentAccess(const Rlist *list, const Policy *policy);
  148 static void KeepControlPromises(EvalContext *ctx, const Policy *policy, GenericAgentConfig *config);
  149 static PromiseResult KeepAgentPromise(EvalContext *ctx, const Promise *pp, void *param);
  150 static void NewTypeContext(TypeSequence type);
  151 static void DeleteTypeContext(EvalContext *ctx, TypeSequence type);
  152 static PromiseResult ParallelFindAndVerifyFilesPromises(EvalContext *ctx, const Promise *pp);
  153 static bool VerifyBootstrap(void);
  154 static void KeepPromiseBundles(EvalContext *ctx, const Policy *policy, GenericAgentConfig *config);
  155 static void KeepPromises(EvalContext *ctx, const Policy *policy, GenericAgentConfig *config);
  156 static int NoteBundleCompliance(const Bundle *bundle, int save_pr_kept, int save_pr_repaired, int save_pr_notkept, struct timespec start);
  157 static void AllClassesReport(const EvalContext *ctx);
  158 static bool HasAvahiSupport(void);
  159 static int AutomaticBootstrap(GenericAgentConfig *config);
  160 static void BannerStatus(PromiseResult status, char *type, char *name);
  161 static PromiseResult DefaultVarPromise(EvalContext *ctx, const Promise *pp);
  162 static void WaitForBackgroundProcesses();
  163 
  164 /*******************************************************************/
  165 /* Command line options                                            */
  166 /*******************************************************************/
  167 
  168 static const char *const CF_AGENT_SHORT_DESCRIPTION =
  169     "evaluate CFEngine policy code and actuate change to the system.";
  170 
  171 static const char *const CF_AGENT_MANPAGE_LONG_DESCRIPTION =
  172         "cf-agent evaluates policy code and makes changes to the system. Policy bundles are evaluated in the order of the "
  173         "provided bundlesequence (this is normally specified in the common control body). "
  174         "For each bundle, cf-agent groups promise statements according to their type. Promise types are then evaluated in a preset "
  175         "order to ensure fast system convergence to policy.\n";
  176 
  177 static const struct option OPTIONS[] =
  178 {
  179     {"bootstrap", required_argument, 0, 'B'},
  180     {"bundlesequence", required_argument, 0, 'b'},
  181     {"workdir", required_argument, 0, 'w'},
  182     {"debug", no_argument, 0, 'd'},
  183     {"define", required_argument, 0, 'D'},
  184     {"self-diagnostics", optional_argument, 0, 'x'},
  185     {"dry-run", no_argument, 0, 'n'},
  186     {"file", required_argument, 0, 'f'},
  187     {"help", no_argument, 0, 'h'},
  188     {"inform", no_argument, 0, 'I'},
  189     {"log-level", required_argument, 0, 'g'},
  190     {"negate", required_argument, 0, 'N'},
  191     {"no-lock", no_argument, 0, 'K'},
  192     {"verbose", no_argument, 0, 'v'},
  193     {"version", no_argument, 0, 'V'},
  194     {"timing-output", no_argument, 0, 't'},
  195     {"trust-server", optional_argument, 0, 'T'},
  196     {"color", optional_argument, 0, 'C'},
  197     {"no-extensions", no_argument, 0, 'E'},
  198     {"timestamp", no_argument, 0, 'l'},
  199     /* Only long option for the rest */
  200     {"log-modules", required_argument, 0, 0},
  201     {"show-evaluated-classes", optional_argument, 0, 0 },
  202     {"show-evaluated-vars", optional_argument, 0, 0 },
  203     {"skip-bootstrap-policy-run", no_argument, 0, 0 },
  204     {"skip-db-check", optional_argument, 0, 0 },
  205     {NULL, 0, 0, '\0'}
  206 };
  207 
  208 static const char *const HINTS[] =
  209 {
  210     "Bootstrap CFEngine to the given policy server IP, hostname or :avahi (automatic detection)",
  211     "Set or override bundlesequence from command line",
  212     "Override the default /var/cfengine work directory for testing (same as setting CFENGINE_TEST_OVERRIDE_WORKDIR)",
  213     "Enable debugging output",
  214     "Define a list of comma separated classes to be defined at the start of execution",
  215     "Run checks to diagnose a CFEngine agent installation",
  216     "All talk and no action mode - make no changes, only inform of promises not kept",
  217     "Specify an alternative input file than the default. This option is overridden by FILE if supplied as argument.",
  218     "Print the help message",
  219     "Print basic information about changes made to the system, i.e. promises repaired",
  220     "Specify how detailed logs should be. Possible values: 'error', 'warning', 'notice', 'info', 'verbose', 'debug'",
  221     "Define a list of comma separated classes to be undefined at the start of execution",
  222     "Ignore locking constraints during execution (ifelapsed/expireafter) if \"too soon\" to run",
  223     "Output verbose information about the behaviour of the agent",
  224     "Output the version of the software",
  225     "Output timing information on console when in verbose mode",
  226     "Possible values: 'yes' (default, trust the server when bootstrapping), 'no' (server key must already be trusted)",
  227     "Enable colorized output. Possible values: 'always', 'auto', 'never'. If option is used, the default value is 'auto'",
  228     "Disable extension loading (used while upgrading)",
  229     "Log timestamps on each line of log output",
  230     "Enable even more detailed debug logging for specific areas of the implementation. Use together with '-d'. Use --log-modules=help for a list of available modules",
  231     "Show *final* evaluated classes, including those defined in common bundles in policy. Optionally can take a regular expression.",
  232     "Show *final* evaluated variables, including those defined without dependency to user-defined classes in policy. Optionally can take a regular expression.",
  233     "Do not run policy as the last step of the bootstrap process",
  234     "Do not run database integrity checks and repairs at startup",
  235     NULL
  236 };
  237 
  238 /*******************************************************************/
  239 
  240 int main(int argc, char *argv[])
  241 {
  242     SetupSignalsForAgent();
  243 #ifdef HAVE_LIBXML2
  244         xmlInitParser();
  245 #endif
  246     struct timespec start = BeginMeasure();
  247 
  248     GenericAgentConfig *config = CheckOpts(argc, argv);
  249     bool force_repair = CheckDBRepairFlagFile();
  250     if (force_repair || PERFORM_DB_CHECK)
  251     {
  252         repair_lmdb_default(force_repair);
  253     }
  254     EvalContext *ctx = EvalContextNew();
  255 
  256     // Enable only for cf-agent eval context.
  257     EvalContextAllClassesLoggingEnable(ctx, true);
  258 
  259     GenericAgentConfigApply(ctx, config);
  260 
  261     const char *program_invocation_name = argv[0];
  262     const char *last_dir_sep = strrchr(program_invocation_name, FILE_SEPARATOR);
  263     const char *program_name = (last_dir_sep != NULL ? last_dir_sep + 1 : program_invocation_name);
  264     GenericAgentDiscoverContext(ctx, config, program_name);
  265 
  266     /* FIXME: (CFE-2709) ALWAYS_VALIDATE will always be false here, since it can
  267      *        only change in KeepPromises(), five lines later on. */
  268     Policy *policy = SelectAndLoadPolicy(config, ctx, ALWAYS_VALIDATE, true);
  269 
  270     if (!policy)
  271     {
  272         Log(LOG_LEVEL_ERR, "Error reading CFEngine policy. Exiting...");
  273         DoCleanupAndExit(EXIT_FAILURE);
  274     }
  275 
  276     int ret = 0;
  277 
  278     GenericAgentPostLoadInit(ctx);
  279     ThisAgentInit();
  280     ConnCache_Init();
  281 
  282     BeginAudit();
  283     KeepPromises(ctx, policy, config);
  284 
  285     if (EvalAborted(ctx))
  286     {
  287         ret = EC_EVAL_ABORTED;
  288     }
  289 
  290     ConnCache_Destroy();
  291 
  292     if (ALLCLASSESREPORT)
  293     {
  294         AllClassesReport(ctx);
  295     }
  296 
  297     Nova_TrackExecution(config->input_file);
  298 
  299     /* Update packages cache. */
  300     UpdatePackagesCache(ctx, false);
  301 
  302     /* Wait for background processes before generating reports because
  303      * GenerateReports() does nothing if it detects multiple cf-agent processes
  304      * running. */
  305     WaitForBackgroundProcesses();
  306 
  307     GenerateReports(config, ctx);
  308 
  309     PurgeLocks();
  310     BackupLockDatabase();
  311 
  312     if (config->agent_specific.agent.show_evaluated_classes != NULL)
  313     {
  314         GenericAgentShowContextsFormatted(ctx, config->agent_specific.agent.show_evaluated_classes);
  315         free(config->agent_specific.agent.show_evaluated_classes);
  316     }
  317 
  318     if (config->agent_specific.agent.show_evaluated_variables != NULL)
  319     {
  320         GenericAgentShowVariablesFormatted(ctx, config->agent_specific.agent.show_evaluated_variables);
  321         free(config->agent_specific.agent.show_evaluated_variables);
  322     }
  323 
  324     PolicyDestroy(policy); /* Can we safely do this earlier ? */
  325     if (config->agent_specific.agent.bootstrap_argument && !VerifyBootstrap())
  326     {
  327         PolicyServerRemoveFile(GetWorkDir());
  328         WriteAmPolicyHubFile(false);
  329         ret = 1;
  330     }
  331 
  332     EndAudit(ctx, CFA_BACKGROUND);
  333 
  334     Nova_NoteAgentExecutionPerformance(config->input_file, start);
  335 
  336     GenericAgentFinalize(ctx, config);
  337     StringSetDestroy(SINGLE_COPY_CACHE);
  338 
  339 #ifdef HAVE_LIBXML2
  340         xmlCleanupParser();
  341 #endif
  342 
  343     return ret;
  344 }
  345 
  346 /*******************************************************************/
  347 /* Level 1                                                         */
  348 /*******************************************************************/
  349 
  350 static void ConfigureBootstrap(GenericAgentConfig *config, const char *argument)
  351 {
  352     assert(config != NULL);
  353     if (!BootstrapAllowed())
  354     {
  355         Log(LOG_LEVEL_ERR, "Not enough privileges to bootstrap CFEngine");
  356         DoCleanupAndExit(EXIT_FAILURE);
  357     }
  358 
  359     if(strcmp(optarg, ":avahi") == 0)
  360     {
  361         if(!HasAvahiSupport())
  362         {
  363             Log(LOG_LEVEL_ERR, "Avahi support is not built in, please see options to the configure script and rebuild CFEngine");
  364             DoCleanupAndExit(EXIT_FAILURE);
  365         }
  366 
  367         int err = AutomaticBootstrap(config);
  368         if (err < 0)
  369         {
  370             Log(LOG_LEVEL_ERR, "Automatic bootstrap failed, error code '%d'", err);
  371             DoCleanupAndExit(EXIT_FAILURE);
  372         }
  373         return;
  374     }
  375 
  376     if(IsLoopbackAddress(argument))
  377     {
  378         Log(LOG_LEVEL_WARNING, "Bootstrapping to loopback interface (localhost), other hosts will not be able to bootstrap to this server");
  379     }
  380 
  381     // temporary assure that network functions are working
  382     OpenNetwork();
  383 
  384     config->agent_specific.agent.bootstrap_argument = xstrdup(argument);
  385 
  386     char *host, *port;
  387     ParseHostPort(optarg, &host, &port);
  388 
  389     char ipaddr[CF_MAX_IP_LEN] = "";
  390     if (Hostname2IPString(ipaddr, host,sizeof(ipaddr)) == -1)
  391     {
  392         Log(LOG_LEVEL_ERR,
  393             "Could not resolve hostname '%s', unable to bootstrap",
  394             host);
  395         DoCleanupAndExit(EXIT_FAILURE);
  396     }
  397 
  398     CloseNetwork();
  399 
  400     MINUSF = true;
  401     config->ignore_locks = true;
  402     GenericAgentConfigSetInputFile(config, GetInputDir(), "promises.cf");
  403 
  404     config->agent_specific.agent.bootstrap_ip = xstrdup(ipaddr);
  405     config->agent_specific.agent.bootstrap_host = xstrdup(host);
  406 
  407     if (port == NULL)
  408     {
  409         config->agent_specific.agent.bootstrap_port = NULL;
  410     }
  411     else
  412     {
  413         config->agent_specific.agent.bootstrap_port = xstrdup(port);
  414     }
  415 }
  416 
  417 static GenericAgentConfig *CheckOpts(int argc, char **argv)
  418 {
  419     extern char *optarg;
  420     int c;
  421 
  422     GenericAgentConfig *config = GenericAgentConfigNewDefault(AGENT_TYPE_AGENT, GetTTYInteractive());
  423     bool option_trust_server = false;
  424 ;
  425 /* DEPRECATED:
  426    --policy-server (-s) is deprecated in community version 3.5.0.
  427    Support rewrite from some common old bootstrap options (until community version 3.6.0?).
  428  */
  429 
  430     int argc_new = argc;
  431     char **argv_tmp = TranslateOldBootstrapOptionsSeparate(&argc_new, argv);
  432     char **argv_new = TranslateOldBootstrapOptionsConcatenated(argc_new, argv_tmp);
  433     FreeFixedStringArray(argc_new, argv_tmp);
  434 
  435     int longopt_idx;
  436     while ((c = getopt_long(argc_new, argv_new, "tdvnKIf:g:w:D:N:VxMB:b:hC::ElT::",
  437                             OPTIONS, &longopt_idx))
  438            != -1)
  439     {
  440         switch (c)
  441         {
  442         case 't':
  443             TIMING = true;
  444             break;
  445 
  446         case 'w':
  447             Log(LOG_LEVEL_INFO, "Setting workdir to '%s'", optarg);
  448             setenv_wrapper("CFENGINE_TEST_OVERRIDE_WORKDIR", optarg, 1);
  449             break;
  450 
  451         case 'f':
  452             GenericAgentConfigSetInputFile(config, GetInputDir(), optarg);
  453             MINUSF = true;
  454             break;
  455 
  456         case 'b':
  457             if (optarg)
  458             {
  459                 Rlist *bundlesequence = RlistFromSplitString(optarg, ',');
  460                 GenericAgentConfigSetBundleSequence(config, bundlesequence);
  461                 RlistDestroy(bundlesequence);
  462             }
  463             break;
  464 
  465         case 'd':
  466             LogSetGlobalLevel(LOG_LEVEL_DEBUG);
  467             break;
  468 
  469         case 'B':
  470             {
  471                 ConfigureBootstrap(config, optarg);
  472             }
  473             break;
  474 
  475         case 'K':
  476             config->ignore_locks = true;
  477             break;
  478 
  479         case 'D':
  480             {
  481                 StringSet *defined_classes = StringSetFromString(optarg, ',');
  482                 if (! config->heap_soft)
  483                 {
  484                     config->heap_soft = defined_classes;
  485                 }
  486                 else
  487                 {
  488                     StringSetJoin(config->heap_soft, defined_classes, xstrdup);
  489                     StringSetDestroy(defined_classes);
  490                 }
  491             }
  492             break;
  493 
  494         case 'N':
  495             {
  496                 StringSet *negated_classes = StringSetFromString(optarg, ',');
  497                 if (! config->heap_negated)
  498                 {
  499                     config->heap_negated = negated_classes;
  500                 }
  501                 else
  502                 {
  503                     StringSetJoin(config->heap_negated, negated_classes, xstrdup);
  504                     StringSetDestroy(negated_classes);
  505                 }
  506             }
  507             break;
  508 
  509         case 'I':
  510             LogSetGlobalLevel(LOG_LEVEL_INFO);
  511             break;
  512 
  513         case 'v':
  514             LogSetGlobalLevel(LOG_LEVEL_VERBOSE);
  515             break;
  516 
  517         case 'g':
  518             LogSetGlobalLevelArgOrExit(optarg);
  519             break;
  520 
  521         case 'n':
  522             DONTDO = true;
  523             config->ignore_locks = true;
  524             break;
  525 
  526         case 'V':
  527             {
  528                 Writer *w = FileWriter(stdout);
  529                 GenericAgentWriteVersion(w);
  530                 FileWriterDetach(w);
  531             }
  532             DoCleanupAndExit(EXIT_SUCCESS);
  533 
  534         case 'h':
  535             {
  536                 Writer *w = FileWriter(stdout);
  537                 WriterWriteHelp(w, "cf-agent", OPTIONS, HINTS, NULL, false, true);
  538                 FileWriterDetach(w);
  539             }
  540             DoCleanupAndExit(EXIT_SUCCESS);
  541 
  542         case 'M':
  543             {
  544                 Writer *out = FileWriter(stdout);
  545                 ManPageWrite(out, "cf-agent", time(NULL),
  546                              CF_AGENT_SHORT_DESCRIPTION,
  547                              CF_AGENT_MANPAGE_LONG_DESCRIPTION,
  548                              OPTIONS, HINTS,
  549                              NULL, false,
  550                              true);
  551                 FileWriterDetach(out);
  552                 DoCleanupAndExit(EXIT_SUCCESS);
  553             }
  554 
  555         case 'x':
  556             {
  557                 const char *workdir = GetWorkDir();
  558                 const char *inputdir = GetInputDir();
  559                 const char *logdir = GetLogDir();
  560                 const char *statedir = GetStateDir();
  561                 Writer *out = FileWriter(stdout);
  562                 WriterWriteF(out, "self-diagnostics for agent using workdir '%s'\n", workdir);
  563                 WriterWriteF(out, "self-diagnostics for agent using inputdir '%s'\n", inputdir);
  564                 WriterWriteF(out, "self-diagnostics for agent using logdir '%s'\n", logdir);
  565                 WriterWriteF(out, "self-diagnostics for agent using statedir '%s'\n", statedir);
  566 
  567                 AgentDiagnosticsRun(workdir, AgentDiagnosticsAllChecks(), out);
  568                 AgentDiagnosticsRunAllChecksNova(workdir, out, &AgentDiagnosticsRun, &AgentDiagnosticsResultNew);
  569                 FileWriterDetach(out);
  570             }
  571             DoCleanupAndExit(EXIT_SUCCESS);
  572 
  573         case 'C':
  574             if (!GenericAgentConfigParseColor(config, optarg))
  575             {
  576                 DoCleanupAndExit(EXIT_FAILURE);
  577             }
  578             break;
  579 
  580         case 'E':
  581             extension_libraries_disable();
  582             break;
  583 
  584         case 'l':
  585             LoggingEnableTimestamps(true);
  586             break;
  587 
  588         case 'T':
  589             option_trust_server = true;
  590 
  591             /* If the argument is missing, we trust by default. */
  592             if (optarg == NULL || strcmp(optarg, "yes") == 0)
  593             {
  594                 config->agent_specific.agent.bootstrap_trust_server = true;
  595             }
  596             else
  597             {
  598                 config->agent_specific.agent.bootstrap_trust_server = false;
  599             }
  600 
  601             break;
  602 
  603         /* long options only */
  604         case 0:
  605         {
  606             const char *const option_name = OPTIONS[longopt_idx].name;
  607             if (StringEqual(option_name, "log-modules"))
  608             {
  609                 bool ret = LogEnableModulesFromString(optarg);
  610                 if (!ret)
  611                 {
  612                     DoCleanupAndExit(EXIT_FAILURE);
  613                 }
  614             }
  615             else if (StringEqual(option_name, "show-evaluated-classes"))
  616             {
  617                 if (optarg == NULL)
  618                 {
  619                     optarg = ".*";
  620                 }
  621                 config->agent_specific.agent.show_evaluated_classes = xstrdup(optarg);
  622             }
  623             else if (StringEqual(option_name, "show-evaluated-vars"))
  624             {
  625                 if (optarg == NULL)
  626                 {
  627                     optarg = ".*";
  628                 }
  629                 config->agent_specific.agent.show_evaluated_variables = xstrdup(optarg);
  630             }
  631             else if (StringEqual(option_name, "skip-bootstrap-policy-run"))
  632             {
  633                 config->agent_specific.agent.bootstrap_trigger_policy = false;
  634             }
  635             else if (StringEqual(option_name, "skip-db-check"))
  636             {
  637                 if (optarg == NULL)
  638                 {
  639                     PERFORM_DB_CHECK = false; // Skip (no arg), check = false
  640                 }
  641                 else if (StringEqual_IgnoreCase(optarg, "yes"))
  642                 {
  643                     PERFORM_DB_CHECK = false; // Skip = yes, check = false
  644                 }
  645                 else if (StringEqual_IgnoreCase(optarg, "no"))
  646                 {
  647                     PERFORM_DB_CHECK = true; // Skip = no, check = true
  648                 }
  649                 else
  650                 {
  651                     Log(LOG_LEVEL_ERR,
  652                         "Invalid argument for --skip-db-check(yes/no): '%s'",
  653                         optarg);
  654                     DoCleanupAndExit(EXIT_FAILURE);
  655                 }
  656             }
  657             break;
  658         }
  659         default:
  660             {
  661                 Writer *w = FileWriter(stdout);
  662                 WriterWriteHelp(w, "cf-agent", OPTIONS, HINTS, NULL, false, true);
  663                 FileWriterDetach(w);
  664             }
  665             DoCleanupAndExit(EXIT_FAILURE);
  666         }
  667     }
  668 
  669     if (!GenericAgentConfigParseArguments(config, argc_new - optind,
  670                                           argv_new + optind))
  671     {
  672         Log(LOG_LEVEL_ERR, "Too many arguments");
  673         DoCleanupAndExit(EXIT_FAILURE);
  674     }
  675 
  676     if (option_trust_server &&
  677         config->agent_specific.agent.bootstrap_argument == NULL)
  678     {
  679         Log(LOG_LEVEL_ERR,
  680             "Option --trust-server can only be used when bootstrapping");
  681         DoCleanupAndExit(EXIT_FAILURE);
  682     }
  683 
  684     FreeFixedStringArray(argc_new, argv_new);
  685 
  686     return config;
  687 }
  688 
  689 
  690 static char **TranslateOldBootstrapOptionsSeparate(int *argc_new, char **argv)
  691 {
  692     int i;
  693     int policy_server_argnum = 0;
  694     int server_address_argnum = 0;
  695     int bootstrap_argnum = 0;
  696     int argc = *argc_new;
  697 
  698     for(i = 0; i < argc; i++)
  699     {
  700         if(strcmp(argv[i], "--policy-server") == 0 || strcmp(argv[i], "-s") == 0)
  701         {
  702             policy_server_argnum = i;
  703         }
  704 
  705         if(strcmp(argv[i], "--bootstrap") == 0 || strcmp(argv[i], "-B") == 0)
  706         {
  707             bootstrap_argnum = i;
  708         }
  709     }
  710 
  711     if(policy_server_argnum > 0)
  712     {
  713         if(policy_server_argnum + 1 < argc)
  714         {
  715             server_address_argnum = policy_server_argnum + 1;
  716         }
  717     }
  718 
  719     char **argv_new;
  720 
  721     if(bootstrap_argnum > 0 && server_address_argnum > 0)
  722     {
  723         Log(LOG_LEVEL_WARNING, "Deprecated bootstrap options detected. The --policy-server (-s) option is deprecated from CFEngine community version 3.5.0."
  724             "Please provide the address argument to --bootstrap (-B) instead. Rewriting your arguments now, but you need to adjust them as this support will be removed soon.");
  725 
  726         *argc_new = argc - 1;  // --policy-server deprecated
  727         argv_new = xcalloc(1, sizeof(char *) * (*argc_new + 1));
  728 
  729         int new_i = 0;
  730 
  731         for(i = 0; i < argc; i++)
  732         {
  733             if(i == bootstrap_argnum)
  734             {
  735                 argv_new[new_i++] = xstrdup(argv[bootstrap_argnum]);
  736                 argv_new[new_i++] = xstrdup(argv[server_address_argnum]);
  737             }
  738             else if(i == server_address_argnum)
  739             {
  740                 // skip: handled above
  741             }
  742             else if(i == policy_server_argnum)
  743             {
  744                 // skip: deprecated
  745             }
  746             else
  747             {
  748                 argv_new[new_i++] = xstrdup(argv[i]);
  749             }
  750         }
  751     }
  752     else
  753     {
  754         argv_new = xcalloc(1, sizeof(char *) * (*argc_new + 1));
  755 
  756         for(i = 0; i < argc; i++)
  757         {
  758             argv_new[i] = xstrdup(argv[i]);
  759         }
  760     }
  761 
  762     return argv_new;
  763 }
  764 
  765 
  766 static char **TranslateOldBootstrapOptionsConcatenated(int argc, char **argv)
  767 {
  768     char **argv_new = xcalloc(1, sizeof(char *) * (argc + 1));
  769 
  770     for(int i = 0; i < argc; i++)
  771     {
  772         if(strcmp(argv[i], "-Bs") == 0)
  773         {
  774             Log(LOG_LEVEL_WARNING, "Deprecated bootstrap options detected. The --policy-server (-s) option is deprecated from CFEngine community version 3.5.0."
  775                 "Please provide the address argument to --bootstrap (-B) instead. Rewriting your arguments now, but you need to adjust them as this support will be removed soon.");
  776             argv_new[i] = xstrdup("-B");
  777         }
  778         else
  779         {
  780             argv_new[i] = xstrdup(argv[i]);
  781         }
  782     }
  783 
  784     return argv_new;
  785 }
  786 
  787 
  788 static void FreeFixedStringArray(int size, char **array)
  789 {
  790     for(int i = 0; i < size; i++)
  791     {
  792         free(array[i]);
  793     }
  794 
  795     free(array);
  796 }
  797 
  798 /*******************************************************************/
  799 
  800 static void ThisAgentInit(void)
  801 {
  802     char filename[CF_BUFSIZE];
  803 
  804 #ifdef HAVE_SETSID
  805     setsid();
  806 #endif
  807 
  808     CFA_MAXTHREADS = 30;
  809     EDITFILESIZE = 100000;
  810 
  811 /*
  812   do not set signal(SIGCHLD,SIG_IGN) in agent near
  813   popen() - or else pclose will fail to return
  814   status which we need for setting returns
  815 */
  816 
  817     snprintf(filename, CF_BUFSIZE, "%s/cfagent.%s.log", GetLogDir(), VSYSNAME.nodename);
  818     ToLowerStrInplace(filename);
  819     MapName(filename);
  820 
  821     const mode_t current_umask = umask(0777);  // Gets and changes umask
  822     umask(current_umask); // Restores umask
  823     Log(LOG_LEVEL_DEBUG, "Current umask is %o", current_umask);
  824     FILE *fp = safe_fopen(filename, "a");
  825     if (fp != NULL)
  826     {
  827         fclose(fp);
  828     }
  829 }
  830 
  831 /*******************************************************************/
  832 
  833 static void KeepPromises(EvalContext *ctx, const Policy *policy, GenericAgentConfig *config)
  834 {
  835     KeepControlPromises(ctx, policy, config);
  836     /* Check if 'abortclasses' aborted evaluation or not. */
  837     if (EvalAborted(ctx))
  838     {
  839         return;
  840     }
  841     KeepPromiseBundles(ctx, policy, config);
  842 }
  843 
  844 /*******************************************************************/
  845 /* Level 2                                                         */
  846 /*******************************************************************/
  847 
  848 static void KeepControlPromises(EvalContext *ctx, const Policy *policy, GenericAgentConfig *config)
  849 {
  850     Seq *constraints = ControlBodyConstraints(policy, AGENT_TYPE_AGENT);
  851     if (constraints)
  852     {
  853         for (size_t i = 0; i < SeqLength(constraints); i++)
  854         {
  855             Constraint *cp = SeqAt(constraints, i);
  856 
  857             if (!IsDefinedClass(ctx, cp->classes))
  858             {
  859                 continue;
  860             }
  861 
  862             if (CommonControlFromString(cp->lval) != COMMON_CONTROL_MAX)
  863             {
  864                 /* Already handled in generic_agent */
  865                 continue;
  866             }
  867 
  868             VarRef *ref = VarRefParseFromScope(cp->lval, "control_agent");
  869             DataType value_type;
  870             const void *value = EvalContextVariableGet(ctx, ref, &value_type);
  871             VarRefDestroy(ref);
  872 
  873             /* If var not found */
  874             if (value_type == CF_DATA_TYPE_NONE)
  875             {
  876                 Log(LOG_LEVEL_ERR, "Unknown lval '%s' in agent control body", cp->lval);
  877                 continue;
  878             }
  879 
  880             /* 'files_single_copy => { }' is a perfectly valid case. */
  881             if (StringEqual(cp->lval, CFA_CONTROLBODY[AGENT_CONTROL_FSINGLECOPY].lval))
  882             {
  883                 assert(value_type == CF_DATA_TYPE_STRING_LIST);
  884                 SINGLE_COPY_LIST = value;
  885                 SINGLE_COPY_CACHE = StringSetNew();
  886                 if (WouldLog(LOG_LEVEL_VERBOSE))
  887                 {
  888                     char *rlist_str = RlistToString(SINGLE_COPY_LIST);
  889                     Log(LOG_LEVEL_VERBOSE, "Setting file single copy list to: %s", rlist_str);
  890                     free(rlist_str);
  891                 }
  892                 continue;
  893             }
  894 
  895             /* Empty list is not supported for the other constraints/attributes. */
  896             if (value == NULL)
  897             {
  898                 Log(LOG_LEVEL_ERR,
  899                     "Empty list is not a valid value for '%s' attribute in agent control body",
  900                     cp->lval);
  901                 continue;
  902             }
  903 
  904             if (strcmp(cp->lval, CFA_CONTROLBODY[AGENT_CONTROL_MAXCONNECTIONS].lval) == 0)
  905             {
  906                 CFA_MAXTHREADS = (int) IntFromString(value);
  907                 Log(LOG_LEVEL_VERBOSE, "Setting maxconnections to %d", CFA_MAXTHREADS);
  908                 continue;
  909             }
  910 
  911             if (strcmp(cp->lval, CFA_CONTROLBODY[AGENT_CONTROL_CHECKSUM_ALERT_TIME].lval) == 0)
  912             {
  913                 CF_PERSISTENCE = (int) IntFromString(value);
  914                 Log(LOG_LEVEL_VERBOSE, "Setting checksum_alert_time to %d", CF_PERSISTENCE);
  915                 continue;
  916             }
  917 
  918             if (strcmp(cp->lval, CFA_CONTROLBODY[AGENT_CONTROL_AGENTFACILITY].lval) == 0)
  919             {
  920                 SetFacility(value);
  921                 continue;
  922             }
  923 
  924             if (strcmp(cp->lval, CFA_CONTROLBODY[AGENT_CONTROL_AGENTACCESS].lval) == 0)
  925             {
  926                 ACCESSLIST = value;
  927                 CheckAgentAccess(ACCESSLIST, policy);
  928                 continue;
  929             }
  930 
  931             if (strcmp(cp->lval, CFA_CONTROLBODY[AGENT_CONTROL_REFRESH_PROCESSES].lval) == 0)
  932             {
  933                 Log(LOG_LEVEL_VERBOSE, "Setting refresh_processes when starting to...");
  934                 for (const Rlist *rp = value; rp != NULL; rp = rp->next)
  935                 {
  936                     Log(LOG_LEVEL_VERBOSE, "%s", RlistScalarValue(rp));
  937                     // TODO: why is this only done in verbose mode?
  938                     // original commit says 'optimization'.
  939                     if (LogGetGlobalLevel() >= LOG_LEVEL_VERBOSE)
  940                     {
  941                         PrependItem(&PROCESSREFRESH, RlistScalarValue(rp), NULL);
  942                     }
  943                 }
  944                 continue;
  945             }
  946 
  947             if (strcmp(cp->lval, CFA_CONTROLBODY[AGENT_CONTROL_ABORTCLASSES].lval) == 0)
  948             {
  949                 Log(LOG_LEVEL_VERBOSE, "Setting abort classes from ...");
  950 
  951                 for (const Rlist *rp = value; rp != NULL; rp = rp->next)
  952                 {
  953                     char name[CF_MAXVARSIZE] = "";
  954 
  955                     strlcpy(name, RlistScalarValue(rp), CF_MAXVARSIZE);
  956 
  957                     EvalContextHeapAddAbort(ctx, name, cp->classes);
  958                 }
  959 
  960                 continue;
  961             }
  962 
  963             if (strcmp(cp->lval, CFA_CONTROLBODY[AGENT_CONTROL_ABORTBUNDLECLASSES].lval) == 0)
  964             {
  965                 Log(LOG_LEVEL_VERBOSE, "Setting abort bundle classes from ...");
  966 
  967                 for (const Rlist *rp = value; rp != NULL; rp = rp->next)
  968                 {
  969                     char name[CF_MAXVARSIZE] = "";
  970                     strlcpy(name, RlistScalarValue(rp), CF_MAXVARSIZE);
  971 
  972                     EvalContextHeapAddAbortCurrentBundle(ctx, name, cp->classes);
  973                 }
  974 
  975                 continue;
  976             }
  977 
  978             if (strcmp(cp->lval, CFA_CONTROLBODY[AGENT_CONTROL_ADDCLASSES].lval) == 0)
  979             {
  980                 Log(LOG_LEVEL_VERBOSE, "Add classes ...");
  981 
  982                 for (const Rlist *rp = value; rp != NULL; rp = rp->next)
  983                 {
  984                     Log(LOG_LEVEL_VERBOSE, "... %s", RlistScalarValue(rp));
  985                     EvalContextClassPutSoft(ctx, RlistScalarValue(rp), CONTEXT_SCOPE_NAMESPACE, "source=environment");
  986                 }
  987 
  988                 continue;
  989             }
  990 
  991             if (strcmp(cp->lval, CFA_CONTROLBODY[AGENT_CONTROL_ALWAYSVALIDATE].lval) == 0)
  992             {
  993                 ALWAYS_VALIDATE = BooleanFromString(value);
  994                 Log(LOG_LEVEL_VERBOSE, "Setting alwaysvalidate to '%s'", ALWAYS_VALIDATE ? "true" : "false");
  995                 continue;
  996             }
  997 
  998             if (strcmp(cp->lval, CFA_CONTROLBODY[AGENT_CONTROL_ALLCLASSESREPORT].lval) == 0)
  999             {
 1000                 ALLCLASSESREPORT = BooleanFromString(value);
 1001                 Log(LOG_LEVEL_VERBOSE, "Setting allclassesreport to '%s'", ALLCLASSESREPORT ? "true" : "false");
 1002             }
 1003 
 1004             if (strcmp(cp->lval, CFA_CONTROLBODY[AGENT_CONTROL_SECUREINPUT].lval) == 0)
 1005             {
 1006                 CFPARANOID = BooleanFromString(value);
 1007                 Log(LOG_LEVEL_VERBOSE, "Setting secure input to '%s'", CFPARANOID ? "true" : "false");
 1008                 continue;
 1009             }
 1010 
 1011             if (strcmp(cp->lval, CFA_CONTROLBODY[AGENT_CONTROL_BINDTOINTERFACE].lval) == 0)
 1012             {
 1013                 SetBindInterface(value);
 1014                 continue;
 1015             }
 1016 
 1017             if (strcmp(cp->lval, CFA_CONTROLBODY[AGENT_CONTROL_HASHUPDATES].lval) == 0)
 1018             {
 1019                 bool enabled = BooleanFromString(value);
 1020 
 1021                 SetChecksumUpdatesDefault(ctx, enabled);
 1022                 Log(LOG_LEVEL_VERBOSE, "Setting checksum updates to '%s'", enabled ? "true" : "false");
 1023                 continue;
 1024             }
 1025 
 1026             if (strcmp(cp->lval, CFA_CONTROLBODY[AGENT_CONTROL_CHILDLIBPATH].lval) == 0)
 1027             {
 1028                 Log(LOG_LEVEL_VERBOSE, "Setting 'LD_LIBRARY_PATH=%s'", (const char *)value);
 1029                 setenv_wrapper("LD_LIBRARY_PATH", value, 1);
 1030                 continue;
 1031             }
 1032 
 1033             if (strcmp(cp->lval, CFA_CONTROLBODY[AGENT_CONTROL_DEFAULTCOPYTYPE].lval) == 0)
 1034             {
 1035                 DEFAULT_COPYTYPE = value;
 1036                 Log(LOG_LEVEL_VERBOSE, "Setting defaultcopytype to '%s'", DEFAULT_COPYTYPE);
 1037                 continue;
 1038             }
 1039 
 1040             if (strcmp(cp->lval, CFA_CONTROLBODY[AGENT_CONTROL_FAUTODEFINE].lval) == 0)
 1041             {
 1042                 SetFileAutoDefineList(value);
 1043                 Log(LOG_LEVEL_VERBOSE, "Setting file auto define list");
 1044                 continue;
 1045             }
 1046 
 1047             if (strcmp(cp->lval, CFA_CONTROLBODY[AGENT_CONTROL_DRYRUN].lval) == 0)
 1048             {
 1049                 DONTDO = BooleanFromString(value);
 1050                 Log(LOG_LEVEL_VERBOSE, "Setting dryrun to %c", DONTDO);
 1051                 continue;
 1052             }
 1053 
 1054             if (strcmp(cp->lval, CFA_CONTROLBODY[AGENT_CONTROL_INFORM].lval) == 0)
 1055             {
 1056                 bool inform = BooleanFromString(value);
 1057                 if (inform)
 1058                 {
 1059                     LogSetGlobalLevel(MAX(LOG_LEVEL_INFO, LogGetGlobalLevel()));
 1060                 }
 1061                 else
 1062                 {
 1063                     if (LogGetGlobalLevel() >= LOG_LEVEL_INFO)
 1064                     {
 1065                         LogSetGlobalLevel(LOG_LEVEL_NOTICE);
 1066                     }
 1067                 }
 1068                 Log(LOG_LEVEL_VERBOSE, "body agent control, inform => '%s', sets new log level to '%s'",
 1069                     inform ? "true" : "false", LogLevelToString(LogGetGlobalLevel()));
 1070                 continue;
 1071             }
 1072 
 1073             if (strcmp(cp->lval, CFA_CONTROLBODY[AGENT_CONTROL_VERBOSE].lval) == 0)
 1074             {
 1075                 bool verbose = BooleanFromString(value);
 1076                 if (verbose)
 1077                 {
 1078                     LogSetGlobalLevel(MAX(LOG_LEVEL_VERBOSE, LogGetGlobalLevel()));
 1079                 }
 1080                 else
 1081                 {
 1082                     if (LogGetGlobalLevel() >= LOG_LEVEL_VERBOSE)
 1083                     {
 1084                         LogSetGlobalLevel(LOG_LEVEL_INFO);
 1085                     }
 1086                 }
 1087                 Log(LOG_LEVEL_VERBOSE, "body agent control, verbose => '%s', sets new log level to '%s'",
 1088                     verbose ? "true" : "false", LogLevelToString(LogGetGlobalLevel()));
 1089                 continue;
 1090             }
 1091 
 1092             if (strcmp(cp->lval, CFA_CONTROLBODY[AGENT_CONTROL_REPOSITORY].lval) == 0)
 1093             {
 1094                 SetRepositoryLocation(value);
 1095                 Log(LOG_LEVEL_VERBOSE, "Setting repository to '%s'", (const char *)value);
 1096                 continue;
 1097             }
 1098 
 1099             if (strcmp(cp->lval, CFA_CONTROLBODY[AGENT_CONTROL_SKIPIDENTIFY].lval) == 0)
 1100             {
 1101                 bool enabled = BooleanFromString(value);
 1102 
 1103                 SetSkipIdentify(enabled);
 1104                 Log(LOG_LEVEL_VERBOSE, "Setting skipidentify to '%s'", enabled ? "true" : "false");
 1105                 continue;
 1106             }
 1107 
 1108             if (strcmp(cp->lval, CFA_CONTROLBODY[AGENT_CONTROL_SUSPICIOUSNAMES].lval) == 0)
 1109             {
 1110                 for (const Rlist *rp = value; rp != NULL; rp = rp->next)
 1111                 {
 1112                     AddFilenameToListOfSuspicious(RlistScalarValue(rp));
 1113                     Log(LOG_LEVEL_VERBOSE, "Considering '%s' as suspicious file", RlistScalarValue(rp));
 1114                 }
 1115 
 1116                 continue;
 1117             }
 1118 
 1119             if (strcmp(cp->lval, CFA_CONTROLBODY[AGENT_CONTROL_REPCHAR].lval) == 0)
 1120             {
 1121                 char c = *(char *)value;
 1122 
 1123                 SetRepositoryChar(c);
 1124                 Log(LOG_LEVEL_VERBOSE, "Setting repchar to '%c'", c);
 1125                 continue;
 1126             }
 1127 
 1128             if (strcmp(cp->lval, CFA_CONTROLBODY[AGENT_CONTROL_MOUNTFILESYSTEMS].lval) == 0)
 1129             {
 1130                 CF_MOUNTALL = BooleanFromString(value);
 1131                 Log(LOG_LEVEL_VERBOSE, "Setting mountfilesystems to '%s'", CF_MOUNTALL ? "true" : "false");
 1132                 continue;
 1133             }
 1134 
 1135             if (strcmp(cp->lval, CFA_CONTROLBODY[AGENT_CONTROL_EDITFILESIZE].lval) == 0)
 1136             {
 1137                 EDITFILESIZE = IntFromString(value);
 1138                 Log(LOG_LEVEL_VERBOSE, "Setting edit file size to %d", EDITFILESIZE);
 1139                 continue;
 1140             }
 1141 
 1142             if (strcmp(cp->lval, CFA_CONTROLBODY[AGENT_CONTROL_IFELAPSED].lval) == 0)
 1143             {
 1144                 VIFELAPSED = IntFromString(value);
 1145                 Log(LOG_LEVEL_VERBOSE, "Setting ifelapsed to %d", VIFELAPSED);
 1146                 continue;
 1147             }
 1148 
 1149             if (strcmp(cp->lval, CFA_CONTROLBODY[AGENT_CONTROL_EXPIREAFTER].lval) == 0)
 1150             {
 1151                 VEXPIREAFTER = IntFromString(value);
 1152                 Log(LOG_LEVEL_VERBOSE, "Setting expireafter to %d", VEXPIREAFTER);
 1153                 continue;
 1154             }
 1155 
 1156             if (strcmp(cp->lval, CFA_CONTROLBODY[AGENT_CONTROL_TIMEOUT].lval) == 0)
 1157             {
 1158                 CONNTIMEOUT = IntFromString(value);
 1159                 Log(LOG_LEVEL_VERBOSE, "Setting timeout to %jd", (intmax_t) CONNTIMEOUT);
 1160                 continue;
 1161             }
 1162 
 1163             if (strcmp(cp->lval, CFA_CONTROLBODY[AGENT_CONTROL_MAX_CHILDREN].lval) == 0)
 1164             {
 1165                 CFA_BACKGROUND_LIMIT = IntFromString(value);
 1166                 Log(LOG_LEVEL_VERBOSE, "Setting max_children to %d", CFA_BACKGROUND_LIMIT);
 1167                 if (CFA_BACKGROUND_LIMIT > 10)
 1168                 {
 1169                     Log(LOG_LEVEL_ERR, "Silly value for max_children in agent control promise (%d > 10)",
 1170                           CFA_BACKGROUND_LIMIT);
 1171                     CFA_BACKGROUND_LIMIT = 1;
 1172                 }
 1173                 continue;
 1174             }
 1175 
 1176             if (strcmp(cp->lval, CFA_CONTROLBODY[AGENT_CONTROL_ENVIRONMENT].lval) == 0)
 1177             {
 1178                 Log(LOG_LEVEL_VERBOSE, "Setting environment variables from ...");
 1179 
 1180                 for (const Rlist *rp = value; rp != NULL; rp = rp->next)
 1181                 {
 1182                     assert(strchr(RlistScalarValue(rp), '=')); /* Valid for putenv() */
 1183                     if (putenv_wrapper(RlistScalarValue(rp)) != 0)
 1184                     {
 1185                         Log(LOG_LEVEL_ERR, "Failed to set environment variable '%s'. (putenv: %s)",
 1186                             RlistScalarValue(rp), GetErrorStr());
 1187                     }
 1188                 }
 1189 
 1190                 continue;
 1191             }
 1192 
 1193             if (strcmp(cp->lval, CFA_CONTROLBODY[AGENT_CONTROL_SELECT_END_MATCH_EOF].lval) == 0)
 1194             {
 1195                 Log(LOG_LEVEL_VERBOSE, "SET select_end_match_eof %s", (char *) value);
 1196                 EvalContextSetSelectEndMatchEof(ctx, BooleanFromString(value));
 1197             }
 1198 
 1199             if (strcmp(cp->lval, CFA_CONTROLBODY[AGENT_CONTROL_REPORTCLASSLOG].lval) == 0)
 1200             {
 1201                 config->agent_specific.agent.report_class_log = BooleanFromString(value);
 1202 
 1203                 Log(LOG_LEVEL_VERBOSE, "Setting report_class_log to %s",
 1204                     config->agent_specific.agent.report_class_log? "true" : "false");
 1205                 continue;
 1206             }
 1207         }
 1208     }
 1209 
 1210     const void *value = NULL;
 1211     if ((value = EvalContextVariableControlCommonGet(ctx, COMMON_CONTROL_LASTSEEN_EXPIRE_AFTER)))
 1212     {
 1213         LASTSEENEXPIREAFTER = IntFromString(value) * 60;
 1214     }
 1215 
 1216     if ((value = EvalContextVariableControlCommonGet(ctx, COMMON_CONTROL_FIPS_MODE)))
 1217     {
 1218         FIPS_MODE = BooleanFromString(value);
 1219         Log(LOG_LEVEL_VERBOSE, "Setting FIPS mode to '%s'", FIPS_MODE ? "true" : "false");
 1220     }
 1221 
 1222     if ((value = EvalContextVariableControlCommonGet(ctx, COMMON_CONTROL_SYSLOG_PORT)))
 1223     {
 1224         SetSyslogPort(IntFromString(value));
 1225         Log(LOG_LEVEL_VERBOSE, "Setting syslog_port to '%s'", (const char *)value);
 1226     }
 1227 
 1228     if ((value = EvalContextVariableControlCommonGet(ctx, COMMON_CONTROL_SYSLOG_HOST)))
 1229     {
 1230         /* Don't resolve syslog_host now, better do it per log request. */
 1231         if (!SetSyslogHost(value))
 1232         {
 1233             Log(LOG_LEVEL_ERR,
 1234                   "Failed to set syslog_host to '%s', too long", (const char *)value);
 1235         }
 1236         else
 1237         {
 1238             Log(LOG_LEVEL_VERBOSE, "Setting syslog_host to '%s'", (const char *)value);
 1239         }
 1240     }
 1241 
 1242     if ((value = EvalContextVariableControlCommonGet(ctx, COMMON_CONTROL_BWLIMIT)))
 1243     {
 1244         double bval;
 1245         if (DoubleFromString(value, &bval))
 1246         {
 1247             bwlimit_kbytes = (uint32_t) ( bval / 1000.0);
 1248             Log(LOG_LEVEL_VERBOSE, "Setting rate limit to %d kBytes/sec", bwlimit_kbytes);
 1249         }
 1250     }
 1251     Nova_Initialize(ctx);
 1252 
 1253     // If not have been enabled above then should be disabled.
 1254     // By default it's enabled to catch all set classes on startup stage
 1255     // before this part of the policy is processed.
 1256     if (!config->agent_specific.agent.report_class_log)
 1257     {
 1258         EvalContextAllClassesLoggingEnable(ctx, false);
 1259     }
 1260 }
 1261 
 1262 /*********************************************************************/
 1263 
 1264 static void KeepPromiseBundles(EvalContext *ctx, const Policy *policy, GenericAgentConfig *config)
 1265 {
 1266     Rlist *bundlesequence = NULL;
 1267 
 1268     Banner("Begin policy/promise evaluation");
 1269 
 1270     if (config->bundlesequence != NULL)
 1271     {
 1272         Log(LOG_LEVEL_INFO, "Using command line specified bundlesequence");
 1273         bundlesequence = RlistCopy(config->bundlesequence);
 1274     }
 1275     else
 1276     {
 1277         bundlesequence = RlistCopy((Rlist *) EvalContextVariableControlCommonGet(
 1278                                        ctx, COMMON_CONTROL_BUNDLESEQUENCE));
 1279 
 1280         if (bundlesequence == NULL)
 1281         {
 1282             RlistAppendScalar(&bundlesequence, "main");
 1283         }
 1284     }
 1285 
 1286     bool ok = true;
 1287     for (const Rlist *rp = bundlesequence; rp; rp = rp->next)
 1288     {
 1289         const char *name = NULL;
 1290 
 1291         switch (rp->val.type)
 1292         {
 1293         case RVAL_TYPE_SCALAR:
 1294             name = RlistScalarValue(rp);
 1295             break;
 1296         case RVAL_TYPE_FNCALL:
 1297             name = RlistFnCallValue(rp)->name;
 1298             break;
 1299 
 1300         default:
 1301             name = NULL;
 1302             {
 1303                 Writer *w = StringWriter();
 1304                 WriterWrite(w, "Illegal item found in bundlesequence: ");
 1305                 RvalWrite(w, rp->val);
 1306                 Log(LOG_LEVEL_ERR, "%s", StringWriterData(w));
 1307                 WriterClose(w);
 1308             }
 1309             ok = false;
 1310             break;
 1311         }
 1312 
 1313         if (!config->ignore_missing_bundles)
 1314         {
 1315             const Bundle *bp = EvalContextResolveBundleExpression(ctx, policy, name, "agent");
 1316             if (!bp)
 1317             {
 1318                 bp = EvalContextResolveBundleExpression(ctx, policy, name, "common");
 1319             }
 1320 
 1321             if (!bp)
 1322             {
 1323                 Log(LOG_LEVEL_ERR, "Bundle '%s' listed in the bundlesequence was not found", name);
 1324                 ok = false;
 1325             }
 1326         }
 1327     }
 1328 
 1329     if (!ok)
 1330     {
 1331         FatalError(ctx, "Errors in agent bundles");
 1332     }
 1333 
 1334     Writer *w = StringWriter();
 1335     WriterWrite(w, "Using bundlesequence => ");
 1336     RlistWrite(w, bundlesequence);
 1337     Log(LOG_LEVEL_VERBOSE, "%s", StringWriterData(w));
 1338     WriterClose(w);
 1339 
 1340 /* If all is okay, go ahead and evaluate */
 1341 
 1342     for (const Rlist *rp = bundlesequence; rp; rp = rp->next)
 1343     {
 1344         const char *name = NULL;
 1345         const Rlist *args = NULL;
 1346 
 1347         switch (rp->val.type)
 1348         {
 1349         case RVAL_TYPE_FNCALL:
 1350             name = RlistFnCallValue(rp)->name;
 1351             args = RlistFnCallValue(rp)->args;
 1352             break;
 1353         default:
 1354             name = RlistScalarValue(rp);
 1355             args = NULL;
 1356             break;
 1357         }
 1358 
 1359         EvalContextSetBundleArgs(ctx, args);
 1360 
 1361         const Bundle *bp = EvalContextResolveBundleExpression(ctx, policy, name, "agent");
 1362         if (!bp)
 1363         {
 1364             bp = EvalContextResolveBundleExpression(ctx, policy, name, "common");
 1365         }
 1366 
 1367         if (bp)
 1368         {
 1369             BundleBanner(bp,args);
 1370             EvalContextStackPushBundleFrame(ctx, bp, args, false);
 1371             ScheduleAgentOperations(ctx, bp);
 1372             EvalContextStackPopFrame(ctx);
 1373             EndBundleBanner(bp);
 1374             if (EvalAborted(ctx))
 1375             {
 1376                 break;
 1377             }
 1378         }
 1379         else
 1380         {
 1381             if (config->ignore_missing_bundles)
 1382             {
 1383                 Log(LOG_LEVEL_VERBOSE, "Ignoring missing bundle '%s'", name);
 1384             }
 1385             else
 1386             {
 1387                 FatalError(ctx, "Bundlesequence contained unknown bundle reference '%s'", name);
 1388             }
 1389         }
 1390     }
 1391 
 1392     RlistDestroy(bundlesequence);
 1393 }
 1394 
 1395 static void AllClassesReport(const EvalContext *ctx)
 1396 {
 1397     char context_report_file[CF_BUFSIZE];
 1398     snprintf(context_report_file, CF_BUFSIZE, "%s%callclasses.txt", GetStateDir(), FILE_SEPARATOR);
 1399 
 1400     FILE *fp = safe_fopen(context_report_file, "w");
 1401     if (fp == NULL)
 1402     {
 1403         Log(LOG_LEVEL_INFO, "Could not open allclasses cache file '%s' (fopen: %s)", context_report_file, GetErrorStr());
 1404     }
 1405     else
 1406     {
 1407         Writer *writer = FileWriter(fp);
 1408         ClassTableIterator *iter = EvalContextClassTableIteratorNewGlobal(ctx, NULL, true, true);
 1409         Class *cls = NULL;
 1410         while ((cls = ClassTableIteratorNext(iter)))
 1411         {
 1412             char *expr = ClassRefToString(cls->ns, cls->name);
 1413             WriterWriteF(writer, "%s\n", expr);
 1414             free(expr);
 1415         }
 1416         ClassTableIteratorDestroy(iter);
 1417         WriterClose(writer);
 1418     }
 1419 }
 1420 
 1421 PromiseResult ScheduleAgentOperations(EvalContext *ctx, const Bundle *bp)
 1422 // NB - this function can be called recursively through "methods"
 1423 {
 1424     int save_pr_kept = PR_KEPT;
 1425     int save_pr_repaired = PR_REPAIRED;
 1426     int save_pr_notkept = PR_NOTKEPT;
 1427     struct timespec start = BeginMeasure();
 1428 
 1429     if (PROCESSREFRESH == NULL || (PROCESSREFRESH && IsRegexItemIn(ctx, PROCESSREFRESH, bp->name)))
 1430     {
 1431         ClearProcessTable();
 1432     }
 1433 
 1434     PromiseResult result = PROMISE_RESULT_SKIPPED;
 1435 
 1436     for (int pass = 1; pass < CF_DONEPASSES; pass++)
 1437     {
 1438         for (TypeSequence type = 0; AGENT_TYPESEQUENCE[type] != NULL; type++)
 1439         {
 1440             const PromiseType *sp = BundleGetPromiseType((Bundle *)bp, AGENT_TYPESEQUENCE[type]);
 1441 
 1442             if (!sp || SeqLength(sp->promises) == 0)
 1443             {
 1444                 continue;
 1445             }
 1446 
 1447             NewTypeContext(type);
 1448 
 1449             SpecialTypeBanner(type, pass);
 1450             EvalContextStackPushPromiseTypeFrame(ctx, sp);
 1451 
 1452             for (size_t ppi = 0; ppi < SeqLength(sp->promises); ppi++)
 1453             {
 1454                 Promise *pp = SeqAt(sp->promises, ppi);
 1455 
 1456                 EvalContextSetPass(ctx, pass);
 1457 
 1458                 PromiseResult promise_result = ExpandPromise(ctx, pp, KeepAgentPromise, NULL);
 1459                 result = PromiseResultUpdate(result, promise_result);
 1460 
 1461                 if (EvalAborted(ctx) || BundleAbort(ctx))
 1462                 {
 1463                     DeleteTypeContext(ctx, type);
 1464                     EvalContextStackPopFrame(ctx);
 1465                     NoteBundleCompliance(bp, save_pr_kept, save_pr_repaired, save_pr_notkept, start);
 1466                     return result;
 1467                 }
 1468             }
 1469 
 1470             DeleteTypeContext(ctx, type);
 1471             EvalContextStackPopFrame(ctx);
 1472 
 1473             if (type == TYPE_SEQUENCE_CONTEXTS)
 1474             {
 1475                 BundleResolve(ctx, bp);
 1476                 BundleResolvePromiseType(ctx, bp, "defaults", (PromiseActuator*)DefaultVarPromise);
 1477             }
 1478         }
 1479     }
 1480 
 1481     NoteBundleCompliance(bp, save_pr_kept, save_pr_repaired, save_pr_notkept, start);
 1482     return result;
 1483 }
 1484 
 1485 /*********************************************************************/
 1486 
 1487 #ifdef __MINGW32__
 1488 
 1489 static void CheckAgentAccess(const Rlist *list, const Policy *policy)
 1490 {
 1491 }
 1492 
 1493 #else
 1494 
 1495 static void CheckAgentAccess(const Rlist *list, const Policy *policy)
 1496 {
 1497     uid_t uid = getuid();
 1498 
 1499     for (const Rlist *rp = list; rp != NULL; rp = rp->next)
 1500     {
 1501         if (Str2Uid(RlistScalarValue(rp), NULL, NULL) == uid)
 1502         {
 1503             return;
 1504         }
 1505     }
 1506 
 1507     {
 1508         StringSet *input_files = PolicySourceFiles(policy);
 1509         StringSetIterator iter = StringSetIteratorInit(input_files);
 1510         const char *input_file = NULL;
 1511         while ((input_file = StringSetIteratorNext(&iter)))
 1512         {
 1513             struct stat sb;
 1514             stat(input_file, &sb);
 1515 
 1516             if (ACCESSLIST)
 1517             {
 1518                 bool access = false;
 1519                 for (const Rlist *rp2 = ACCESSLIST; rp2 != NULL; rp2 = rp2->next)
 1520                 {
 1521                     if (Str2Uid(RlistScalarValue(rp2), NULL, NULL) == sb.st_uid)
 1522                     {
 1523                         access = true;
 1524                         break;
 1525                     }
 1526                 }
 1527 
 1528                 if (!access)
 1529                 {
 1530                     Log(LOG_LEVEL_ERR, "File '%s' is not owned by an authorized user (security exception)", input_file);
 1531                     DoCleanupAndExit(EXIT_FAILURE);
 1532                 }
 1533             }
 1534             else if (CFPARANOID && IsPrivileged())
 1535             {
 1536                 if (sb.st_uid != getuid())
 1537                 {
 1538                     Log(LOG_LEVEL_ERR, "File '%s' is not owned by uid %ju (security exception)", input_file,
 1539                           (uintmax_t)getuid());
 1540                     DoCleanupAndExit(EXIT_FAILURE);
 1541                 }
 1542             }
 1543         }
 1544 
 1545         StringSetDestroy(input_files);
 1546     }
 1547 
 1548     Log(LOG_LEVEL_ERR, "You are denied access to run this policy");
 1549     DoCleanupAndExit(EXIT_FAILURE);
 1550 }
 1551 #endif /* !__MINGW32__ */
 1552 
 1553 /*********************************************************************/
 1554 
 1555 static PromiseResult DefaultVarPromise(EvalContext *ctx, const Promise *pp)
 1556 {
 1557     char *regex = PromiseGetConstraintAsRval(pp, "if_match_regex", RVAL_TYPE_SCALAR);
 1558     bool okay = true;
 1559 
 1560 
 1561     DataType value_type = CF_DATA_TYPE_NONE;
 1562     const void *value = NULL;
 1563     {
 1564         VarRef *ref = VarRefParseFromScope(pp->promiser, "this");
 1565         value = EvalContextVariableGet(ctx, ref, &value_type);
 1566         VarRefDestroy(ref);
 1567     }
 1568 
 1569     switch (value_type)
 1570     {
 1571     case CF_DATA_TYPE_STRING:
 1572     case CF_DATA_TYPE_INT:
 1573     case CF_DATA_TYPE_REAL:
 1574         if (regex && !FullTextMatch(ctx, regex, value))
 1575         {
 1576             return PROMISE_RESULT_NOOP;
 1577         }
 1578 
 1579         if (regex == NULL)
 1580         {
 1581             return PROMISE_RESULT_NOOP;
 1582         }
 1583         break;
 1584 
 1585     case CF_DATA_TYPE_STRING_LIST:
 1586     case CF_DATA_TYPE_INT_LIST:
 1587     case CF_DATA_TYPE_REAL_LIST:
 1588         if (regex)
 1589         {
 1590             for (const Rlist *rp = value; rp != NULL; rp = rp->next)
 1591             {
 1592                 if (FullTextMatch(ctx, regex, RlistScalarValue(rp)))
 1593                 {
 1594                     okay = false;
 1595                     break;
 1596                 }
 1597             }
 1598 
 1599             if (okay)
 1600             {
 1601                 return PROMISE_RESULT_NOOP;
 1602             }
 1603         }
 1604         break;
 1605 
 1606     default:
 1607         break;
 1608     }
 1609 
 1610     {
 1611         VarRef *ref = VarRefParseFromBundle(pp->promiser, PromiseGetBundle(pp));
 1612         EvalContextVariableRemove(ctx, ref);
 1613         VarRefDestroy(ref);
 1614     }
 1615 
 1616     return VerifyVarPromise(ctx, pp, NULL);
 1617 }
 1618 
 1619 static void LogVariableValue(const EvalContext *ctx, const Promise *pp)
 1620 {
 1621     VarRef *ref = VarRefParseFromBundle(pp->promiser, PromiseGetBundle(pp));
 1622     char *out = NULL;
 1623 
 1624     DataType type;
 1625     const void *var = EvalContextVariableGet(ctx, ref, &type);
 1626     switch (type)
 1627     {
 1628         case CF_DATA_TYPE_INT:
 1629         case CF_DATA_TYPE_REAL:
 1630         case CF_DATA_TYPE_STRING:
 1631             out = xstrdup((char *) var);
 1632             break;
 1633         case CF_DATA_TYPE_INT_LIST:
 1634         case CF_DATA_TYPE_REAL_LIST:
 1635         case CF_DATA_TYPE_STRING_LIST:
 1636         {
 1637             size_t siz = CF_BUFSIZE;
 1638             size_t len = 0;
 1639             out = xcalloc(1, CF_BUFSIZE);
 1640 
 1641             for (Rlist *rp = (Rlist *) var; rp != NULL; rp = rp->next)
 1642             {
 1643                 const char *s = (char *) rp->val.item;
 1644 
 1645                 if (strlen(s) + len + 3  >= siz)                // ", " + NULL
 1646                 {
 1647                     out = xrealloc(out, siz + CF_BUFSIZE);
 1648                     siz += CF_BUFSIZE;
 1649                 }
 1650 
 1651                 if (len > 0)
 1652                 {
 1653                     len += strlcat(out, ", ", siz);
 1654                 }
 1655 
 1656                 len += strlcat(out, s, siz);
 1657             }
 1658             break;
 1659         }
 1660         case CF_DATA_TYPE_CONTAINER:
 1661         {
 1662             Writer *w = StringWriter();
 1663             JsonWriteCompact(w, (JsonElement *) var);
 1664             out = StringWriterClose(w);
 1665             break;
 1666         }
 1667         default:
 1668             /* TODO is CF_DATA_TYPE_NONE acceptable? Today all meta variables
 1669              * are of this type. */
 1670             /* UnexpectedError("Variable '%s' is of unknown type %d", */
 1671             /*                 pp->promiser, type); */
 1672             out = xstrdup("NONE");
 1673             break;
 1674     }
 1675 
 1676     Log(LOG_LEVEL_DEBUG, "V: '%s' => '%s'", pp->promiser, out);
 1677     free(out);
 1678     VarRefDestroy(ref);
 1679 }
 1680 
 1681 static PromiseResult KeepAgentPromise(EvalContext *ctx, const Promise *pp, ARG_UNUSED void *param)
 1682 {
 1683     assert(param == NULL);
 1684     struct timespec start = BeginMeasure();
 1685     PromiseResult result = PROMISE_RESULT_NOOP;
 1686 
 1687     if (strcmp("meta", pp->parent_promise_type->name) == 0 ||
 1688         strcmp("vars", pp->parent_promise_type->name) == 0)
 1689     {
 1690         Log(LOG_LEVEL_VERBOSE, "V:     Computing value of '%s'", pp->promiser);
 1691 
 1692         result = VerifyVarPromise(ctx, pp, NULL);
 1693         if (result != PROMISE_RESULT_FAIL)
 1694         {
 1695             if (LogGetGlobalLevel() >= LOG_LEVEL_DEBUG)
 1696             {
 1697                 LogVariableValue(ctx, pp);
 1698             }
 1699         }
 1700     }
 1701     else if (strcmp("defaults", pp->parent_promise_type->name) == 0)
 1702     {
 1703         result = DefaultVarPromise(ctx, pp);
 1704     }
 1705     else if (strcmp("classes", pp->parent_promise_type->name) == 0)
 1706     {
 1707         result = VerifyClassPromise(ctx, pp, NULL);
 1708     }
 1709     else if (strcmp("processes", pp->parent_promise_type->name) == 0)
 1710     {
 1711         if (!LoadProcessTable())
 1712         {
 1713             Log(LOG_LEVEL_ERR, "Unable to read the process table - cannot keep processes: type promises");
 1714             return PROMISE_RESULT_FAIL;
 1715         }
 1716         result = VerifyProcessesPromise(ctx, pp);
 1717         if (result != PROMISE_RESULT_SKIPPED)
 1718         {
 1719             EndMeasurePromise(start, pp);
 1720         }
 1721     }
 1722     else if (strcmp("storage", pp->parent_promise_type->name) == 0)
 1723     {
 1724         result = FindAndVerifyStoragePromises(ctx, pp);
 1725         if (result != PROMISE_RESULT_SKIPPED)
 1726         {
 1727             EndMeasurePromise(start, pp);
 1728         }
 1729     }
 1730     else if (strcmp("packages", pp->parent_promise_type->name) == 0)
 1731     {
 1732         result = VerifyPackagesPromise(ctx, pp);
 1733         if (result != PROMISE_RESULT_SKIPPED)
 1734         {
 1735             EndMeasurePromise(start, pp);
 1736         }
 1737     }
 1738     else if (strcmp("users", pp->parent_promise_type->name) == 0)
 1739     {
 1740         result = VerifyUsersPromise(ctx, pp);
 1741         if (result != PROMISE_RESULT_SKIPPED)
 1742         {
 1743             EndMeasurePromise(start, pp);
 1744         }
 1745     }
 1746 
 1747     else if (strcmp("files", pp->parent_promise_type->name) == 0)
 1748     {
 1749         result = ParallelFindAndVerifyFilesPromises(ctx, pp);
 1750         if (result != PROMISE_RESULT_SKIPPED)
 1751         {
 1752             EndMeasurePromise(start, pp);
 1753         }
 1754     }
 1755     else if (strcmp("commands", pp->parent_promise_type->name) == 0)
 1756     {
 1757         result = VerifyExecPromise(ctx, pp);
 1758         if (result != PROMISE_RESULT_SKIPPED)
 1759         {
 1760             EndMeasurePromise(start, pp);
 1761         }
 1762     }
 1763     else if (strcmp("databases", pp->parent_promise_type->name) == 0)
 1764     {
 1765         result = VerifyDatabasePromises(ctx, pp);
 1766         if (result != PROMISE_RESULT_SKIPPED)
 1767         {
 1768             EndMeasurePromise(start, pp);
 1769         }
 1770     }
 1771     else if (strcmp("methods", pp->parent_promise_type->name) == 0)
 1772     {
 1773         result = VerifyMethodsPromise(ctx, pp);
 1774         if (result != PROMISE_RESULT_SKIPPED)
 1775         {
 1776             EndMeasurePromise(start, pp);
 1777         }
 1778     }
 1779     else if (strcmp("services", pp->parent_promise_type->name) == 0)
 1780     {
 1781         result = VerifyServicesPromise(ctx, pp);
 1782         if (result != PROMISE_RESULT_SKIPPED)
 1783         {
 1784             EndMeasurePromise(start, pp);
 1785         }
 1786     }
 1787     else if (strcmp("guest_environments", pp->parent_promise_type->name) == 0)
 1788     {
 1789         result = VerifyEnvironmentsPromise(ctx, pp);
 1790         if (result != PROMISE_RESULT_SKIPPED)
 1791         {
 1792             EndMeasurePromise(start, pp);
 1793         }
 1794     }
 1795     else if (strcmp("reports", pp->parent_promise_type->name) == 0)
 1796     {
 1797         result = VerifyReportPromise(ctx, pp);
 1798     }
 1799     else
 1800     {
 1801         result = PROMISE_RESULT_NOOP;
 1802     }
 1803 
 1804     BannerStatus(result, pp->parent_promise_type->name, pp->promiser);
 1805     EvalContextLogPromiseIterationOutcome(ctx, pp, result);
 1806     return result;
 1807 }
 1808 
 1809 
 1810 static void BannerStatus(PromiseResult status, char *type, char *name)
 1811 {
 1812     if ((strcmp(type, "vars") == 0) || (strcmp(type, "classes") == 0))
 1813     {
 1814         return;
 1815     }
 1816 
 1817     switch (status)
 1818     {
 1819     case PROMISE_RESULT_CHANGE:
 1820         Log(LOG_LEVEL_VERBOSE, "A: Promise REPAIRED");
 1821         break;
 1822 
 1823     case PROMISE_RESULT_TIMEOUT:
 1824         Log(LOG_LEVEL_VERBOSE, "A: Promise TIMED-OUT");
 1825         break;
 1826 
 1827     case PROMISE_RESULT_WARN:
 1828     case PROMISE_RESULT_FAIL:
 1829     case PROMISE_RESULT_INTERRUPTED:
 1830         Log(LOG_LEVEL_VERBOSE, "A: Promise NOT KEPT!");
 1831         break;
 1832 
 1833     case PROMISE_RESULT_DENIED:
 1834         Log(LOG_LEVEL_VERBOSE, "A: Promise NOT KEPT - denied");
 1835         break;
 1836 
 1837     case PROMISE_RESULT_NOOP:
 1838         Log(LOG_LEVEL_VERBOSE, "A: Promise was KEPT");
 1839         break;
 1840     default:
 1841         return;
 1842         break;
 1843     }
 1844 
 1845     Log(LOG_LEVEL_VERBOSE, "P: END %s promise (%.30s%s)",
 1846         type, name,
 1847         (strlen(name) > 30) ? "..." : "");
 1848 }
 1849 
 1850 /*********************************************************************/
 1851 /* Type context                                                      */
 1852 /*********************************************************************/
 1853 
 1854 static void NewTypeContext(TypeSequence type)
 1855 {
 1856 // get maxconnections
 1857 
 1858     switch (type)
 1859     {
 1860     case TYPE_SEQUENCE_ENVIRONMENTS:
 1861         NewEnvironmentsContext();
 1862         break;
 1863 
 1864     case TYPE_SEQUENCE_FILES:
 1865         break;
 1866 
 1867     case TYPE_SEQUENCE_PROCESSES:
 1868         break;
 1869 
 1870     case TYPE_SEQUENCE_STORAGE:
 1871 #ifndef __MINGW32__                   // TODO: Run if implemented on Windows
 1872         if (SeqLength(GetGlobalMountedFSList()))
 1873         {
 1874             DeleteMountInfo(GetGlobalMountedFSList());
 1875             SeqClear(GetGlobalMountedFSList());
 1876         }
 1877 #endif /* !__MINGW32__ */
 1878         break;
 1879 
 1880     default:
 1881         break;
 1882     }
 1883 
 1884     return;
 1885 }
 1886 
 1887 /*********************************************************************/
 1888 
 1889 static void DeleteTypeContext(EvalContext *ctx, TypeSequence type)
 1890 {
 1891     switch (type)
 1892     {
 1893     case TYPE_SEQUENCE_ENVIRONMENTS:
 1894         DeleteEnvironmentsContext();
 1895         break;
 1896 
 1897     case TYPE_SEQUENCE_FILES:
 1898         break;
 1899 
 1900     case TYPE_SEQUENCE_PROCESSES:
 1901         break;
 1902 
 1903     case TYPE_SEQUENCE_STORAGE:
 1904         DeleteStorageContext();
 1905         break;
 1906 
 1907     case TYPE_SEQUENCE_PACKAGES:
 1908         ExecuteScheduledPackages(ctx);
 1909         CleanScheduledPackages();
 1910         break;
 1911 
 1912     default:
 1913         break;
 1914     }
 1915 }
 1916 
 1917 /**************************************************************/
 1918 /* Thread context                                             */
 1919 /**************************************************************/
 1920 
 1921 #ifdef __MINGW32__
 1922 
 1923 static PromiseResult ParallelFindAndVerifyFilesPromises(EvalContext *ctx, const Promise *pp)
 1924 {
 1925     int background = PromiseGetConstraintAsBoolean(ctx, "background", pp);
 1926 
 1927     if (background)
 1928     {
 1929         Log(LOG_LEVEL_VERBOSE, "Background processing of files promises is not supported on Windows");
 1930     }
 1931 
 1932     return FindAndVerifyFilesPromises(ctx, pp);
 1933 }
 1934 
 1935 #else /* !__MINGW32__ */
 1936 
 1937 static PromiseResult ParallelFindAndVerifyFilesPromises(EvalContext *ctx, const Promise *pp)
 1938 {
 1939     int background = PromiseGetConstraintAsBoolean(ctx, "background", pp);
 1940     pid_t child = 1;
 1941     PromiseResult result = PROMISE_RESULT_SKIPPED;
 1942 
 1943     if (background)
 1944     {
 1945         if (CFA_BACKGROUND < CFA_BACKGROUND_LIMIT)
 1946         {
 1947             CFA_BACKGROUND++;
 1948             Log(LOG_LEVEL_VERBOSE, "Spawning new process...");
 1949             child = fork();
 1950 
 1951             if (child == 0)
 1952             {
 1953                 ALARM_PID = -1;
 1954 
 1955                 result = PromiseResultUpdate(result, FindAndVerifyFilesPromises(ctx, pp));
 1956 
 1957                 Log(LOG_LEVEL_VERBOSE, "Exiting backgrounded promise");
 1958                 PromiseRef(LOG_LEVEL_VERBOSE, pp);
 1959                 _exit(EXIT_SUCCESS);
 1960                 // TODO: need to solve this
 1961             }
 1962         }
 1963         else
 1964         {
 1965             Log(LOG_LEVEL_VERBOSE, "Promised parallel execution promised but exceeded the max number of promised background tasks, so serializing");
 1966             background = 0;
 1967         }
 1968     }
 1969     else
 1970     {
 1971         result = PromiseResultUpdate(result, FindAndVerifyFilesPromises(ctx, pp));
 1972     }
 1973 
 1974     return result;
 1975 }
 1976 
 1977 #endif /* !__MINGW32__ */
 1978 
 1979 /**************************************************************/
 1980 
 1981 static bool VerifyBootstrap(void)
 1982 {
 1983     const char *policy_server = PolicyServerGet();
 1984     if (NULL_OR_EMPTY(policy_server))
 1985     {
 1986         Log(LOG_LEVEL_ERR, "Bootstrapping failed, no policy server is specified");
 1987         return false;
 1988     }
 1989 
 1990     // we should at least have gotten promises.cf from the policy hub
 1991     {
 1992         char filename[CF_MAXVARSIZE];
 1993         snprintf(filename, sizeof(filename), "%s/promises.cf", GetInputDir());
 1994         MapName(filename);
 1995 
 1996         struct stat sb;
 1997         if (stat(filename, &sb) == -1)
 1998         {
 1999             Log(LOG_LEVEL_ERR, "Bootstrapping failed, no input file at '%s' after bootstrap", filename);
 2000             return false;
 2001         }
 2002     }
 2003 
 2004     // embedded failsafe.cf (bootstrap.c) contains a promise to start cf-execd (executed while running this cf-agent)
 2005     ClearProcessTable();
 2006     LoadProcessTable();
 2007 
 2008     if (!IsProcessNameRunning(".*cf-execd.*"))
 2009     {
 2010         Log(LOG_LEVEL_ERR, "Bootstrapping failed, cf-execd is not running");
 2011         return false;
 2012     }
 2013 
 2014 
 2015     Log(LOG_LEVEL_NOTICE, "Bootstrap to '%s' completed successfully!", policy_server);
 2016     return true;
 2017 }
 2018 
 2019 /**************************************************************/
 2020 /* Compliance comp                                            */
 2021 /**************************************************************/
 2022 
 2023 static int NoteBundleCompliance(const Bundle *bundle, int save_pr_kept, int save_pr_repaired, int save_pr_notkept, struct timespec start)
 2024 {
 2025     double delta_pr_kept, delta_pr_repaired, delta_pr_notkept;
 2026     double bundle_compliance = 0.0;
 2027 
 2028     delta_pr_kept = (double) (PR_KEPT - save_pr_kept);
 2029     delta_pr_notkept = (double) (PR_NOTKEPT - save_pr_notkept);
 2030     delta_pr_repaired = (double) (PR_REPAIRED - save_pr_repaired);
 2031 
 2032     Log(LOG_LEVEL_VERBOSE, "A: ...................................................");
 2033     Log(LOG_LEVEL_VERBOSE, "A: Bundle Accounting Summary for '%s' in namespace %s", bundle->name, bundle->ns);
 2034 
 2035     if (delta_pr_kept + delta_pr_notkept + delta_pr_repaired <= 0)
 2036     {
 2037         Log(LOG_LEVEL_VERBOSE, "A: Zero promises executed for bundle '%s'", bundle->name);
 2038         Log(LOG_LEVEL_VERBOSE, "A: ...................................................");
 2039         return PROMISE_RESULT_NOOP;
 2040     }
 2041     else
 2042     {
 2043         Log(LOG_LEVEL_VERBOSE, "A: Promises kept in '%s' = %.0lf", bundle->name, delta_pr_kept);
 2044         Log(LOG_LEVEL_VERBOSE, "A: Promises not kept in '%s' = %.0lf", bundle->name, delta_pr_notkept);
 2045         Log(LOG_LEVEL_VERBOSE, "A: Promises repaired in '%s' = %.0lf", bundle->name, delta_pr_repaired);
 2046 
 2047         bundle_compliance = (delta_pr_kept + delta_pr_repaired) / (delta_pr_kept + delta_pr_notkept + delta_pr_repaired);
 2048 
 2049         Log(LOG_LEVEL_VERBOSE, "A: Aggregate compliance (promises kept/repaired) for bundle '%s' = %.1lf%%",
 2050           bundle->name, bundle_compliance * 100.0);
 2051 
 2052         if (LogGetGlobalLevel() >= LOG_LEVEL_INFO)
 2053         {
 2054             char name[CF_MAXVARSIZE];
 2055             snprintf(name, CF_MAXVARSIZE, "%s:%s", bundle->ns, bundle->name);
 2056             EndMeasure(name, start);
 2057         }
 2058         else
 2059         {
 2060             EndMeasure(NULL, start);
 2061         }
 2062         Log(LOG_LEVEL_VERBOSE, "A: ...................................................");
 2063     }
 2064 
 2065     // return the worst case for the bundle status
 2066 
 2067     if (delta_pr_notkept > 0)
 2068     {
 2069         return PROMISE_RESULT_FAIL;
 2070     }
 2071 
 2072     if (delta_pr_repaired > 0)
 2073     {
 2074         return PROMISE_RESULT_CHANGE;
 2075     }
 2076 
 2077     return PROMISE_RESULT_NOOP;
 2078 }
 2079 
 2080 #if defined(HAVE_AVAHI_CLIENT_CLIENT_H) && defined(HAVE_AVAHI_COMMON_ADDRESS_H)
 2081 
 2082 static bool HasAvahiSupport(void)
 2083 {
 2084     return true;
 2085 }
 2086 
 2087 
 2088 static int AutomaticBootstrap(GenericAgentConfig *config)
 2089 {
 2090     List *foundhubs = NULL;
 2091     int hubcount = ListHubs(&foundhubs);
 2092     int ret;
 2093 
 2094     switch(hubcount)
 2095     {
 2096     case -1:
 2097         Log(LOG_LEVEL_ERR, "Error while trying to find a Policy Server");
 2098         ret = -1;
 2099         break;
 2100     case 0:
 2101         Log(LOG_LEVEL_ERR, "No hubs were found. Exiting.");
 2102         ret = -1;
 2103         break;
 2104     case 1:
 2105     {
 2106         char *hostname = ((HostProperties*)foundhubs)->Hostname;
 2107         char *ipaddr = ((HostProperties*)foundhubs)->IPAddress;
 2108         Log(LOG_LEVEL_NOTICE, "Autodiscovered hub installed on hostname '%s', IP address '%s'",
 2109             hostname, ipaddr);
 2110 
 2111         // TODO: This is a very bad way to check for valid IP(?)
 2112         if (strlen(ipaddr) < CF_MAX_IP_LEN)
 2113         {
 2114             config->agent_specific.agent.bootstrap_argument = xstrdup(ipaddr);
 2115             config->agent_specific.agent.bootstrap_ip       = xstrdup(ipaddr);
 2116             config->agent_specific.agent.bootstrap_host     = xstrdup(ipaddr);
 2117             ret = 0;
 2118         }
 2119         else
 2120         {
 2121             Log(LOG_LEVEL_ERR,  "Invalid autodiscovered hub IP address '%s'", ipaddr);
 2122             ret = -1;
 2123         }
 2124         break;
 2125     }
 2126     default:
 2127         Log(LOG_LEVEL_ERR, "Found more than one hub registered in the network. Please bootstrap manually using IP from the list below.");
 2128         PrintList(foundhubs);
 2129         ret = -1;
 2130     };
 2131 
 2132     if (avahi_handle)
 2133     {
 2134         /*
 2135          * This case happens when dlopen does not manage to open the library.
 2136          */
 2137         dlclose(avahi_handle);
 2138     }
 2139     ListDestroy(&foundhubs);
 2140 
 2141     return ret;
 2142 }
 2143 #else
 2144 
 2145 static bool HasAvahiSupport(void)
 2146 {
 2147     return false;
 2148 }
 2149 
 2150 static int AutomaticBootstrap(ARG_UNUSED GenericAgentConfig *config)
 2151 {
 2152     ProgrammingError("Attempted automated bootstrap on a non-avahi build of CFEngine");
 2153 }
 2154 
 2155 #endif // Avahi
 2156 
 2157 static void WaitForBackgroundProcesses()
 2158 {
 2159 #ifdef __MINGW32__
 2160     /* no fork() on Windows */
 2161     return;
 2162 #else
 2163     Log(LOG_LEVEL_VERBOSE, "Waiting for background processes");
 2164     bool have_children = true;
 2165     while (have_children)
 2166     {
 2167         pid_t child = wait(NULL);
 2168         if (child >= 0)
 2169         {
 2170             Log(LOG_LEVEL_VERBOSE, "Background process %ju terminated", (uintmax_t) child);
 2171         }
 2172         have_children = !((child == -1) && (errno == ECHILD));
 2173     }
 2174     Log(LOG_LEVEL_VERBOSE, "No more background processes to wait for");
 2175     return;
 2176 #endif  /* __MINGW32__ */
 2177 }