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
26 #include <monitoring_read.h>
27
28 #include <file_lib.h> /* FILE_SEPARATOR */
29 #include <known_dirs.h>
30
31
32 /* GLOBALS */
33
34 static const char *UNITS[CF_OBSERVABLES] = /* constant */
35 {
36 "average users per 2.5 mins",
37 "processes",
38 "processes",
39 "percent",
40 "jobs",
41 "connections",
42 "connections",
43 "connections",
44 "connections",
45 "connections",
46 "connections",
47 "connections",
48 "connections",
49 "connections",
50 "connections",
51 "connections",
52 "connections",
53 "connections",
54 "connections",
55 "connections",
56 "connections",
57 "connections",
58 "connections",
59 "connections",
60 "connections",
61 "connections",
62 "connections",
63 "packets",
64 "packets",
65 "packets",
66 "packets",
67 "packets",
68 "packets",
69 "packets",
70 "packets",
71 "packets",
72 "packets",
73 "packets",
74 "packets",
75 "packets",
76 "packets",
77 "entries",
78 "entries",
79 "entries",
80 "entries",
81 "Celcius",
82 "Celcius",
83 "Celcius",
84 "Celcius",
85 "percent",
86 "percent",
87 "percent",
88 "percent",
89 "percent",
90 "packets",
91 "packets",
92 "connections",
93 "connections",
94 "connections",
95 "connections",
96 "connections",
97 "connections",
98 "connections",
99 "connections",
100
101 };
102
103 time_t slots_load_time = 0;
104 MonitoringSlot *SLOTS[CF_OBSERVABLES - ob_spare];
105
106
107 /*****************************************************************************/
108
Nova_FreeSlot(MonitoringSlot * slot)109 void Nova_FreeSlot(MonitoringSlot *slot)
110 {
111 if (slot)
112 {
113 free(slot->name);
114 free(slot->description);
115 free(slot->units);
116 free(slot);
117 }
118 }
119
Nova_MakeSlot(const char * name,const char * description,const char * units,double expected_minimum,double expected_maximum,bool consolidable)120 MonitoringSlot *Nova_MakeSlot(const char *name, const char *description,
121 const char *units,
122 double expected_minimum, double expected_maximum,
123 bool consolidable)
124 {
125 MonitoringSlot *slot = xmalloc(sizeof(MonitoringSlot));
126
127 slot->name = xstrdup(name);
128 slot->description = xstrdup(description);
129 slot->units = xstrdup(units);
130 slot->expected_minimum = expected_minimum;
131 slot->expected_maximum = expected_maximum;
132 slot->consolidable = consolidable;
133 return slot;
134 }
135
136 // This function is similar to cf-check/observables.c function GetObservableNames
137 // If we refactor for dynamic observables instead of hard coded and limited to 100
138 // then we likely should change here and there or refactor to have
139 // this ts_key read/parse code in one shared place.
Nova_LoadSlots(void)140 void Nova_LoadSlots(void)
141 {
142 char filename[CF_BUFSIZE];
143 int i;
144
145 snprintf(filename, CF_BUFSIZE - 1, "%s%cts_key", GetStateDir(), FILE_SEPARATOR);
146
147 FILE *f = safe_fopen(filename, "r");
148 if (f == NULL)
149 {
150 return;
151 }
152
153 int fd = fileno(f);
154 struct stat sb;
155 if ((fstat(fd, &sb) != 0) ||
156 (sb.st_mtime <= slots_load_time))
157 {
158 fclose(f);
159 return;
160 }
161
162 slots_load_time = sb.st_mtime;
163
164 for (i = 0; i < CF_OBSERVABLES; ++i)
165 {
166 if (i < ob_spare)
167 {
168 int r;
169 do
170 {
171 r = fgetc(f);
172 } while (r != (int)'\n' && r != EOF);
173 if (r == EOF)
174 {
175 break;
176 }
177 }
178 else
179 {
180 char line[CF_MAXVARSIZE];
181
182 char name[CF_MAXVARSIZE], desc[CF_MAXVARSIZE];
183 char units[CF_MAXVARSIZE] = "unknown";
184 double expected_min = 0.0;
185 double expected_max = 100.0;
186 int consolidable = true;
187
188 if (fgets(line, CF_MAXVARSIZE, f) == NULL)
189 {
190 Log(LOG_LEVEL_ERR, "Error trying to read ts_key from file '%s'. (fgets: %s)", filename, GetErrorStr());
191 break;
192 }
193
194 int fields = sscanf(line, "%*d,%1023[^,],%1023[^,],%1023[^,],%lf,%lf,%d",
195 name, desc, units, &expected_min, &expected_max, &consolidable);
196
197 if (fields == 2)
198 {
199 /* Old-style ts_key with name and description */
200 }
201 else if (fields == 6)
202 {
203 /* New-style ts_key with additional parameters */
204 }
205 else
206 {
207 Log(LOG_LEVEL_ERR, "Wrong line format in ts_key: %s", line);
208 }
209
210 if (strcmp(name, "spare") != 0)
211 {
212 Nova_FreeSlot(SLOTS[i - ob_spare]);
213 SLOTS[i - ob_spare] = Nova_MakeSlot(name, desc, units, expected_min, expected_max, consolidable);
214 }
215 }
216 }
217 fclose(f);
218 }
219
220
NovaHasSlot(int idx)221 bool NovaHasSlot(int idx)
222 {
223 Nova_LoadSlots();
224
225 return idx < ob_spare || SLOTS[idx - ob_spare];
226 }
227
NovaGetSlotName(int idx)228 const char *NovaGetSlotName(int idx)
229 {
230 Nova_LoadSlots();
231
232 return idx < ob_spare ? OBSERVABLES[idx][0] : SLOTS[idx - ob_spare]->name;
233 }
234
NovaGetSlotDescription(int idx)235 const char *NovaGetSlotDescription(int idx)
236 {
237 Nova_LoadSlots();
238
239 return idx < ob_spare ? OBSERVABLES[idx][1] : SLOTS[idx - ob_spare]->description;
240 }
241
NovaGetSlotUnits(int idx)242 const char *NovaGetSlotUnits(int idx)
243 {
244 Nova_LoadSlots();
245
246 return idx < ob_spare ? UNITS[idx] : SLOTS[idx - ob_spare]->units;
247 }
248
249 // TODO: real expected minimum/maximum/consolidable for core slots
250
NovaGetSlotExpectedMinimum(int idx)251 double NovaGetSlotExpectedMinimum(int idx)
252 {
253 Nova_LoadSlots();
254
255 return idx < ob_spare ? 0.0f : SLOTS[idx - ob_spare]->expected_minimum;
256 }
257
NovaGetSlotExpectedMaximum(int idx)258 double NovaGetSlotExpectedMaximum(int idx)
259 {
260 Nova_LoadSlots();
261
262 return idx < ob_spare ? 100.0f : SLOTS[idx - ob_spare]->expected_maximum;
263 }
264
NovaIsSlotConsolidable(int idx)265 bool NovaIsSlotConsolidable(int idx)
266 {
267 Nova_LoadSlots();
268
269 return idx < ob_spare ? true : SLOTS[idx - ob_spare]->consolidable;
270 }
271
272
273
274
275 /*
276 * This function returns beginning of last Monday relative to 'time'. If 'time'
277 * is Monday, beginning of the same day is returned.
278 */
WeekBegin(time_t time)279 time_t WeekBegin(time_t time)
280 {
281 struct tm tm;
282
283 gmtime_r(&time, &tm);
284
285 /* Move back in time to reach Monday. */
286
287 time -= ((tm.tm_wday == 0 ? 6 : tm.tm_wday - 1) * SECONDS_PER_DAY);
288
289 /* Move to the beginning of day */
290
291 time -= tm.tm_hour * SECONDS_PER_HOUR;
292 time -= tm.tm_min * SECONDS_PER_MINUTE;
293 time -= tm.tm_sec;
294
295 return time;
296 }
297
SubtractWeeks(time_t time,int weeks)298 time_t SubtractWeeks(time_t time, int weeks)
299 {
300 return time - weeks * SECONDS_PER_WEEK;
301 }
302
NextShift(time_t time)303 time_t NextShift(time_t time)
304 {
305 return time + SECONDS_PER_SHIFT;
306 }
307
308
MakeTimekey(time_t time,char * result)309 void MakeTimekey(time_t time, char *result)
310 {
311 /* Generate timekey for database */
312 struct tm tm;
313
314 gmtime_r(&time, &tm);
315
316 snprintf(result, 64, "%d_%.3s_Lcycle_%d_%s",
317 tm.tm_mday, MONTH_TEXT[tm.tm_mon], (tm.tm_year + 1900) % 3, SHIFT_TEXT[tm.tm_hour / 6]);
318 }
319
320 /* Returns true if entry was found, false otherwise */
GetRecordForTime(CF_DB * db,time_t time,Averages * result)321 bool GetRecordForTime(CF_DB *db, time_t time, Averages *result)
322 {
323 char timekey[CF_MAXVARSIZE];
324
325 MakeTimekey(time, timekey);
326
327 return ReadDB(db, timekey, result, sizeof(Averages));
328 }
329
330