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