1 /*
2   Copyright 2021 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 #include <generic_agent.h>
26 #include <mon.h>
27 
28 #include <eval_context.h>
29 #include <env_monitor.h>
30 #include <conversion.h>
31 #include <vars.h>
32 #include <signals.h>
33 #include <scope.h>
34 #include <known_dirs.h>
35 #include <man.h>
36 #include <bootstrap.h>
37 #include <timeout.h>
38 #include <time_classes.h>
39 #include <loading.h>
40 #include <cleanup.h>
41 #include <file_lib.h>           /* FILE_SEPARATOR */
42 
43 typedef enum
44 {
45     MONITOR_CONTROL_FORGET_RATE,
46     MONITOR_CONTROL_MONITOR_FACILITY,
47     MONITOR_CONTROL_HISTOGRAMS,
48     MONITOR_CONTROL_TCP_DUMP,
49     MONITOR_CONTROL_NONE
50 } MonitorControl;
51 
52 static void ThisAgentInit(EvalContext *ctx);
53 static GenericAgentConfig *CheckOpts(int argc, char **argv);
54 static void KeepPromises(EvalContext *ctx, const Policy *policy);
55 
56 /*****************************************************************************/
57 /* Globals                                                                   */
58 /*****************************************************************************/
59 
60 extern int NO_FORK;
61 
62 extern const ConstraintSyntax CFM_CONTROLBODY[];
63 
64 /*******************************************************************/
65 /* Command line options                                            */
66 /*******************************************************************/
67 
68 static const char *const CF_MONITORD_SHORT_DESCRIPTION =
69     "monitoring daemon for CFEngine";
70 
71 static const char *const CF_MONITORD_MANPAGE_LONG_DESCRIPTION =
72     "cf-monitord is the monitoring daemon for CFEngine. It samples probes defined in policy code and attempts to learn the "
73     "normal system state based on current and past observations. Current estimates are made available as "
74     "special variables (e.g. $(mon.av_cpu)) to cf-agent, which may use them to inform policy decisions.";
75 
76 static const struct option OPTIONS[] =
77 {
78     {"help", no_argument, 0, 'h'},
79     {"debug", no_argument, 0, 'd'},
80     {"verbose", no_argument, 0, 'v'},
81     {"dry-run", no_argument, 0, 'n'},
82     {"version", no_argument, 0, 'V'},
83     {"no-lock", no_argument, 0, 'K'},
84     {"file", required_argument, 0, 'f'},
85     {"log-level", required_argument, 0, 'g'},
86     {"inform", no_argument, 0, 'I'},
87     {"diagnostic", no_argument, 0, 'x'},
88     {"no-fork", no_argument, 0, 'F'},
89     {"histograms", no_argument, 0, 'H'},
90     {"tcpdump", no_argument, 0, 'T'},
91     {"color", optional_argument, 0, 'C'},
92     {"timestamp", no_argument, 0, 'l'},
93     /* Only long option for the rest */
94     {"ignore-preferred-augments", no_argument, 0, 0},
95     {NULL, 0, 0, '\0'}
96 };
97 
98 static const char *const HINTS[] =
99 {
100     "Print the help message",
101     "Enable debugging output",
102     "Output verbose information about the behaviour of cf-monitord",
103     "All talk and no action mode - make no changes, only inform of promises not kept",
104     "Output the version of the software",
105     "Ignore system lock",
106     "Specify an alternative input file than the default. This option is overridden by FILE if supplied as argument.",
107     "Specify how detailed logs should be. Possible values: 'error', 'warning', 'notice', 'info', 'verbose', 'debug'",
108     "Print basic information about changes made to the system, i.e. promises repaired",
109     "Activate internal diagnostics (developers only)",
110     "Run process in foreground, not as a daemon",
111     "Ignored for backward compatibility",
112     "Interface with tcpdump if available to collect data about network",
113     "Enable colorized output. Possible values: 'always', 'auto', 'never'. If option is used, the default value is 'auto'",
114     "Log timestamps on each line of log output",
115     "Ignore def_preferred.json file in favor of def.json",
116     NULL
117 };
118 
119 /*****************************************************************************/
120 
main(int argc,char * argv[])121 int main(int argc, char *argv[])
122 {
123     GenericAgentConfig *config = CheckOpts(argc, argv);
124     EvalContext *ctx = EvalContextNew();
125     GenericAgentConfigApply(ctx, config);
126 
127     const char *program_invocation_name = argv[0];
128     const char *last_dir_sep = strrchr(program_invocation_name, FILE_SEPARATOR);
129     const char *program_name = (last_dir_sep != NULL ? last_dir_sep + 1 : program_invocation_name);
130     GenericAgentDiscoverContext(ctx, config, program_name);
131 
132     Policy *policy = LoadPolicy(ctx, config);
133 
134     GenericAgentPostLoadInit(ctx);
135     ThisAgentInit(ctx);
136 
137     KeepPromises(ctx, policy);
138 
139     MonitorStartServer(ctx, policy);
140 
141     PolicyDestroy(policy);
142     GenericAgentFinalize(ctx, config);
143     CallCleanupFunctions();
144     return 0;
145 }
146 
147 /*******************************************************************/
148 
CheckOpts(int argc,char ** argv)149 static GenericAgentConfig *CheckOpts(int argc, char **argv)
150 {
151     extern char *optarg;
152     int c;
153     GenericAgentConfig *config = GenericAgentConfigNewDefault(AGENT_TYPE_MONITOR, GetTTYInteractive());
154 
155     int longopt_idx;
156     while ((c = getopt_long(argc, argv, "dvnIf:g:VSxHTKMFhC::l",
157                             OPTIONS, &longopt_idx)) != -1)
158     {
159         switch (c)
160         {
161         case 'f':
162             GenericAgentConfigSetInputFile(config, GetInputDir(), optarg);
163             MINUSF = true;
164             break;
165 
166         case 'd':
167             LogSetGlobalLevel(LOG_LEVEL_DEBUG);
168             NO_FORK = true;
169             break;
170 
171         case 'K':
172             config->ignore_locks = true;
173             break;
174 
175         case 'I':
176             LogSetGlobalLevel(LOG_LEVEL_INFO);
177             break;
178 
179         case 'v':
180             LogSetGlobalLevel(LOG_LEVEL_VERBOSE);
181             NO_FORK = true;
182             break;
183 
184         case 'g':
185             LogSetGlobalLevelArgOrExit(optarg);
186             break;
187 
188         case 'F':
189             NO_FORK = true;
190             break;
191 
192         case 'H':              /* Keep accepting this option for compatibility -- no longer used */
193             break;
194 
195         case 'T':
196             MonNetworkSnifferEnable(true);
197             break;
198 
199         case 'V':
200         {
201             Writer *w = FileWriter(stdout);
202             GenericAgentWriteVersion(w);
203             FileWriterDetach(w);
204         }
205         DoCleanupAndExit(EXIT_SUCCESS);
206 
207         case 'h':
208         {
209             Writer *w = FileWriter(stdout);
210             WriterWriteHelp(w, "cf-monitord", OPTIONS, HINTS, NULL, false, true);
211             FileWriterDetach(w);
212         }
213         DoCleanupAndExit(EXIT_SUCCESS);
214 
215         case 'M':
216         {
217             Writer *out = FileWriter(stdout);
218             ManPageWrite(out, "cf-monitord", time(NULL),
219                          CF_MONITORD_SHORT_DESCRIPTION,
220                          CF_MONITORD_MANPAGE_LONG_DESCRIPTION,
221                          OPTIONS, HINTS,
222                          NULL, false,
223                          true);
224             FileWriterDetach(out);
225             DoCleanupAndExit(EXIT_SUCCESS);
226         }
227 
228         case 'x':
229             Log(LOG_LEVEL_ERR, "Self-diagnostic functionality is retired.");
230             DoCleanupAndExit(EXIT_SUCCESS);
231 
232         case 'C':
233             if (!GenericAgentConfigParseColor(config, optarg))
234             {
235                 DoCleanupAndExit(EXIT_FAILURE);
236             }
237             break;
238 
239         case 'l':
240             LoggingEnableTimestamps(true);
241             break;
242 
243         /* long options only */
244         case 0:
245         {
246             const char *const option_name = OPTIONS[longopt_idx].name;
247             if (StringEqual(option_name, "ignore-preferred-augments"))
248             {
249                 config->ignore_preferred_augments = true;
250             }
251             break;
252         }
253 
254         default:
255         {
256             Writer *w = FileWriter(stdout);
257             WriterWriteHelp(w, "cf-monitord", OPTIONS, HINTS, NULL, false, true);
258             FileWriterDetach(w);
259         }
260         DoCleanupAndExit(EXIT_FAILURE);
261         }
262     }
263 
264     if (!GenericAgentConfigParseArguments(config, argc - optind, argv + optind))
265     {
266         Log(LOG_LEVEL_ERR, "Too many arguments");
267         DoCleanupAndExit(EXIT_FAILURE);
268     }
269 
270     return config;
271 }
272 
273 /*****************************************************************************/
274 
KeepPromises(EvalContext * ctx,const Policy * policy)275 static void KeepPromises(EvalContext *ctx, const Policy *policy)
276 {
277     Seq *constraints = ControlBodyConstraints(policy, AGENT_TYPE_MONITOR);
278     if (constraints)
279     {
280         for (size_t i = 0; i < SeqLength(constraints); i++)
281         {
282             Constraint *cp = SeqAt(constraints, i);
283 
284             if (!IsDefinedClass(ctx, cp->classes))
285             {
286                 continue;
287             }
288 
289             VarRef *ref = VarRefParseFromScope(cp->lval, "control_monitor");
290             const void *value = EvalContextVariableGet(ctx, ref, NULL);
291             VarRefDestroy(ref);
292             if (!value)
293             {
294                 Log(LOG_LEVEL_ERR, "Unknown lval '%s' in monitor control body", cp->lval);
295                 continue;
296             }
297 
298             if (strcmp(cp->lval, CFM_CONTROLBODY[MONITOR_CONTROL_HISTOGRAMS].lval) == 0)
299             {
300                 /* Keep accepting this option for backward compatibility. */
301             }
302 
303             if (strcmp(cp->lval, CFM_CONTROLBODY[MONITOR_CONTROL_TCP_DUMP].lval) == 0)
304             {
305                 MonNetworkSnifferEnable(BooleanFromString(value));
306             }
307 
308             if (strcmp(cp->lval, CFM_CONTROLBODY[MONITOR_CONTROL_FORGET_RATE].lval) == 0)
309             {
310                 sscanf(value, "%lf", &FORGETRATE);
311                 Log(LOG_LEVEL_DEBUG, "forget rate %f", FORGETRATE);
312             }
313         }
314     }
315 }
316 
317 /*****************************************************************************/
318 /* Level 1                                                                   */
319 /*****************************************************************************/
320 
ThisAgentInit(EvalContext * ctx)321 static void ThisAgentInit(EvalContext *ctx)
322 {
323     umask(077);
324     strcpy(VPREFIX, "cf-monitord");
325 
326     time_t t = SetReferenceTime();
327     UpdateTimeClasses(ctx, t);
328 
329     signal(SIGINT, HandleSignalsForDaemon);
330     signal(SIGTERM, HandleSignalsForDaemon);
331     signal(SIGBUS, HandleSignalsForDaemon);
332     signal(SIGHUP, SIG_IGN);
333     signal(SIGPIPE, SIG_IGN);
334     signal(SIGUSR1, HandleSignalsForDaemon);
335     signal(SIGUSR2, HandleSignalsForDaemon);
336 
337     FORGETRATE = 0.6;
338 
339     MonitorInitialize();
340 }
341