1 /*
2   Copyright 2020 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 
Nova_LoadSlots(void)136 void Nova_LoadSlots(void)
137 {
138     char filename[CF_BUFSIZE];
139     int i;
140 
141     snprintf(filename, CF_BUFSIZE - 1, "%s%cts_key", GetStateDir(), FILE_SEPARATOR);
142 
143     FILE *f = safe_fopen(filename, "r");
144     if (f == NULL)
145     {
146         return;
147     }
148 
149     int fd = fileno(f);
150     struct stat sb;
151     if ((fstat(fd, &sb) != 0) ||
152         (sb.st_mtime <= slots_load_time))
153     {
154         fclose(f);
155         return;
156     }
157 
158     slots_load_time = sb.st_mtime;
159 
160     for (i = 0; i < CF_OBSERVABLES; ++i)
161     {
162         if (i < ob_spare)
163         {
164             int r;
165             do
166             {
167                 r = fgetc(f);
168             } while (r != (int)'\n' && r != EOF);
169             if (r == EOF)
170             {
171                 break;
172             }
173         }
174         else
175         {
176             char line[CF_MAXVARSIZE];
177 
178             char name[CF_MAXVARSIZE], desc[CF_MAXVARSIZE];
179             char units[CF_MAXVARSIZE] = "unknown";
180             double expected_min = 0.0;
181             double expected_max = 100.0;
182             int consolidable = true;
183 
184             if (fgets(line, CF_MAXVARSIZE, f) == NULL)
185             {
186                 Log(LOG_LEVEL_ERR, "Error trying to read ts_key from file '%s'. (fgets: %s)", filename, GetErrorStr());
187                 break;
188             }
189 
190             int fields = sscanf(line, "%*d,%1023[^,],%1023[^,],%1023[^,],%lf,%lf,%d",
191                                 name, desc, units, &expected_min, &expected_max, &consolidable);
192 
193             if (fields == 2)
194             {
195                 /* Old-style ts_key with name and description */
196             }
197             else if (fields == 6)
198             {
199                 /* New-style ts_key with additional parameters */
200             }
201             else
202             {
203                 Log(LOG_LEVEL_ERR, "Wrong line format in ts_key: %s", line);
204             }
205 
206             if (strcmp(name, "spare") != 0)
207             {
208                 Nova_FreeSlot(SLOTS[i - ob_spare]);
209                 SLOTS[i - ob_spare] = Nova_MakeSlot(name, desc, units, expected_min, expected_max, consolidable);
210             }
211         }
212     }
213     fclose(f);
214 }
215 
216 
NovaHasSlot(int idx)217 bool NovaHasSlot(int idx)
218 {
219     Nova_LoadSlots();
220 
221     return idx < ob_spare || SLOTS[idx - ob_spare];
222 }
223 
NovaGetSlotName(int idx)224 const char *NovaGetSlotName(int idx)
225 {
226     Nova_LoadSlots();
227 
228     return idx < ob_spare ? OBSERVABLES[idx][0] : SLOTS[idx - ob_spare]->name;
229 }
230 
NovaGetSlotDescription(int idx)231 const char *NovaGetSlotDescription(int idx)
232 {
233     Nova_LoadSlots();
234 
235     return idx < ob_spare ? OBSERVABLES[idx][1] : SLOTS[idx - ob_spare]->description;
236 }
237 
NovaGetSlotUnits(int idx)238 const char *NovaGetSlotUnits(int idx)
239 {
240     Nova_LoadSlots();
241 
242     return idx < ob_spare ? UNITS[idx] : SLOTS[idx - ob_spare]->units;
243 }
244 
245 // TODO: real expected minimum/maximum/consolidable for core slots
246 
NovaGetSlotExpectedMinimum(int idx)247 double NovaGetSlotExpectedMinimum(int idx)
248 {
249     Nova_LoadSlots();
250 
251     return idx < ob_spare ? 0.0f : SLOTS[idx - ob_spare]->expected_minimum;
252 }
253 
NovaGetSlotExpectedMaximum(int idx)254 double NovaGetSlotExpectedMaximum(int idx)
255 {
256     Nova_LoadSlots();
257 
258     return idx < ob_spare ? 100.0f : SLOTS[idx - ob_spare]->expected_maximum;
259 }
260 
NovaIsSlotConsolidable(int idx)261 bool NovaIsSlotConsolidable(int idx)
262 {
263     Nova_LoadSlots();
264 
265     return idx < ob_spare ? true : SLOTS[idx - ob_spare]->consolidable;
266 }
267 
268 
269 
270 
271 /*
272  * This function returns beginning of last Monday relative to 'time'. If 'time'
273  * is Monday, beginning of the same day is returned.
274  */
WeekBegin(time_t time)275 time_t WeekBegin(time_t time)
276 {
277     struct tm tm;
278 
279     gmtime_r(&time, &tm);
280 
281     /* Move back in time to reach Monday. */
282 
283     time -= ((tm.tm_wday == 0 ? 6 : tm.tm_wday - 1) * SECONDS_PER_DAY);
284 
285     /* Move to the beginning of day */
286 
287     time -= tm.tm_hour * SECONDS_PER_HOUR;
288     time -= tm.tm_min * SECONDS_PER_MINUTE;
289     time -= tm.tm_sec;
290 
291     return time;
292 }
293 
SubtractWeeks(time_t time,int weeks)294 time_t SubtractWeeks(time_t time, int weeks)
295 {
296     return time - weeks * SECONDS_PER_WEEK;
297 }
298 
NextShift(time_t time)299 time_t NextShift(time_t time)
300 {
301     return time + SECONDS_PER_SHIFT;
302 }
303 
304 
MakeTimekey(time_t time,char * result)305 void MakeTimekey(time_t time, char *result)
306 {
307     /* Generate timekey for database */
308     struct tm tm;
309 
310     gmtime_r(&time, &tm);
311 
312     snprintf(result, 64, "%d_%.3s_Lcycle_%d_%s",
313              tm.tm_mday, MONTH_TEXT[tm.tm_mon], (tm.tm_year + 1900) % 3, SHIFT_TEXT[tm.tm_hour / 6]);
314 }
315 
316 /* Returns true if entry was found, false otherwise */
GetRecordForTime(CF_DB * db,time_t time,Averages * result)317 bool GetRecordForTime(CF_DB *db, time_t time, Averages *result)
318 {
319     char timekey[CF_MAXVARSIZE];
320 
321     MakeTimekey(time, timekey);
322 
323     return ReadDB(db, timekey, result, sizeof(Averages));
324 }
325 
326