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