Coverage Report

Created: 2026-10-28 06:53

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ntopng/src/MostVisitedList.cpp
Line
Count
Source
1
/*
2
 *
3
 * (C) 2013-26 - ntop.org
4
 *
5
 *
6
 * This program is free software; you can redistribute it and/or modify
7
 * it under the terms of the GNU General Public License as published by
8
 * the Free Software Foundation; either version 3 of the License, or
9
 * (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License
17
 * along with this program; if not, write to the Free Software Foundation,
18
 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19
 *
20
 */
21
22
#include "ntop_includes.h"
23
24
/* *************************************** */
25
26
6.43k
MostVisitedList::MostVisitedList(u_int32_t _max_num_items) {
27
6.43k
  if (trace_new_delete)
28
0
    ntop->getTrace()->traceEvent(TRACE_NORMAL, "[new] %s", __FILE__);
29
6.43k
  top_data = new (std::nothrow) FrequentStringItems(_max_num_items);
30
6.43k
  old_data = shadow_old_data = NULL;
31
6.43k
  max_num_items = _max_num_items;
32
6.43k
  current_cycle = 0;
33
6.43k
}
34
35
/* *************************************** */
36
37
6.43k
MostVisitedList::~MostVisitedList() {
38
6.43k
  if (trace_new_delete)
39
0
    ntop->getTrace()->traceEvent(TRACE_NORMAL, "[delete] %s", __FILE__);
40
6.43k
  if (top_data) delete (top_data);
41
6.43k
  if (old_data) free(old_data);
42
6.43k
  if (shadow_old_data) free(shadow_old_data);
43
6.43k
}
44
45
/* *************************************** */
46
47
12
void MostVisitedList::getCurrentTime(struct tm* t_now) {
48
12
  time_t now = time(NULL);
49
50
12
  memset(t_now, 0, sizeof(*t_now));
51
12
  localtime_r(&now, t_now);
52
12
}
53
54
/* *************************************** */
55
56
void MostVisitedList::saveOldData(u_int32_t iface_id, char* additional_key_info,
57
0
                                  char* hashkey) {
58
0
  char redis_key[64];
59
0
  int minute = 0;
60
0
  struct tm t_now;
61
62
  // Using a shadow due to a possible segv while freeing
63
  // old_data and getting stats from lua
64
0
  if (shadow_old_data) {
65
0
    free(shadow_old_data);
66
0
    shadow_old_data = NULL;
67
0
  }
68
69
0
  if (!ntop->getRedis()) return;
70
71
  /* Still no old data collected */
72
0
  if (!old_data) {
73
0
    old_data = top_data->json();
74
0
    return;
75
0
  }
76
77
0
  getCurrentTime(&t_now);
78
0
  minute = t_now.tm_min - (t_now.tm_min % 5);
79
80
0
  snprintf(redis_key, sizeof(redis_key), "%s%s_%d_%d_%d_%d",
81
0
           (char*)NTOPNG_CACHE_PREFIX, additional_key_info, iface_id,
82
0
           t_now.tm_mday, t_now.tm_hour, minute);
83
84
  /* String like `ntopng.cache.1_17_11_45` */
85
  /* An other way is to use the localtime_r and compose the string like
86
   * `ntopng.cache_2_1609761600` */
87
88
0
  ntop->getRedis()->set(redis_key, old_data, 7200);
89
90
0
  if (minute == 0 && current_cycle > 0) {
91
0
    char hour_done[64];
92
0
    int hour = 0;
93
94
0
    if (t_now.tm_hour == 0)
95
0
      hour = 23;
96
0
    else
97
0
      hour = t_now.tm_hour - 1;
98
99
    /* List key = ntopng.cache.top_sites_hour_done | value = 1_17_11 */
100
0
    snprintf(hour_done, sizeof(hour_done), "%d_%d_%d", iface_id, t_now.tm_mday,
101
0
             hour);
102
103
0
    ntop->getRedis()->lpush(hashkey, hour_done, 0);
104
105
0
    current_cycle = 0;
106
0
  } else
107
0
    current_cycle++;
108
109
0
  shadow_old_data = old_data;
110
0
  old_data = top_data->json();
111
0
}
112
113
/* *************************************** */
114
115
0
void MostVisitedList::lua(lua_State* vm, char* name, char* old_name) {
116
0
  FrequentStringItems* cur_top_data = top_data;
117
0
  char* cur_old_data = old_data;
118
119
0
  if (cur_top_data) {
120
0
    char* cur_top_data_json = cur_top_data->json();
121
122
0
    if (cur_top_data_json) {
123
0
      lua_push_str_table_entry(vm, name, cur_top_data_json);
124
0
      free(cur_top_data_json);
125
0
    }
126
0
  }
127
128
0
  if (cur_old_data) lua_push_str_table_entry(vm, old_name, cur_old_data);
129
0
}
130
131
/* *************************************** */
132
133
void MostVisitedList::serializeDeserialize(u_int32_t iface_id,
134
                                           bool do_serialize, char* extra_info,
135
                                           char* info_subject,
136
                                           char* hour_hashkey,
137
12
                                           char* day_hashkey) {
138
12
  struct tm t_now;
139
12
  char redis_hour_key[64], redis_daily_key[64], redis_key_current_data[64];
140
141
12
  if (!top_data) return;
142
143
  /* Struct containing the current time */
144
12
  getCurrentTime(&t_now);
145
146
  /* Formatting the hour and daily redis key */
147
12
  snprintf(redis_hour_key, sizeof(redis_hour_key), "%s%u_%d_%u", extra_info,
148
12
           iface_id, t_now.tm_mday, t_now.tm_hour);
149
12
  snprintf(redis_daily_key, sizeof(redis_daily_key), "%s%u_%d", extra_info,
150
12
           iface_id, t_now.tm_mday);
151
152
12
  snprintf(redis_key_current_data, sizeof(redis_key_current_data), "%s%s%d_%d",
153
12
           (char*)NTOPNG_CACHE_PREFIX, info_subject, iface_id, t_now.tm_mday);
154
155
  /* Serialize the data */
156
12
  if (do_serialize) {
157
6
    ntop->getRedis()->lpush(hour_hashkey, redis_hour_key, 0);
158
6
    ntop->getRedis()->lpush(day_hashkey, redis_daily_key, 0);
159
6
    if (top_data->getSize()) {
160
      /* Serialize the double of the max value */
161
0
      char* top_data_json = top_data->json(2 * max_num_items);
162
163
0
      if (top_data_json) {
164
0
        ntop->getRedis()->set(redis_key_current_data, top_data_json, 3600);
165
0
        free(top_data_json);
166
0
      }
167
0
    }
168
6
  }
169
  /* Deserialize the data */
170
6
  else {
171
6
    ntop->getRedis()->lrem(hour_hashkey, redis_hour_key);
172
6
    ntop->getRedis()->lrem(day_hashkey, redis_daily_key);
173
6
    deserializeTopData(redis_key_current_data);
174
6
    ntop->getRedis()->del(redis_key_current_data);
175
6
  }
176
12
}
177
178
/* *************************************** */
179
180
6
void MostVisitedList::deserializeTopData(char* redis_key_current) {
181
6
  char* json;
182
6
  u_int json_len;
183
6
  json_object* j;
184
6
  enum json_tokener_error jerr;
185
186
6
  json_len = ntop->getRedis()->len(redis_key_current);
187
6
  if (json_len == 0)
188
6
    json_len = CONST_MAX_LEN_REDIS_VALUE;
189
0
  else
190
0
    json_len += 8; /* Little overhead */
191
192
6
  if ((json = (char*)malloc(json_len)) == NULL) {
193
0
    ntop->getTrace()->traceEvent(TRACE_WARNING, "Not enough memory");
194
0
    return;
195
0
  }
196
197
6
  if ((ntop->getRedis()->get(redis_key_current, json, json_len) == -1) ||
198
6
      (json[0] == '\0')) {
199
6
    free(json);
200
6
    return; /* Nothing found */
201
6
  }
202
203
0
  j = json_tokener_parse_verbose(json, &jerr);
204
205
0
  if (j != NULL) {
206
#ifdef DEBUG
207
    ntop->getTrace()->traceEvent(TRACE_NORMAL, "%s [%u]", json, json_len);
208
#endif
209
210
0
    if (json_object_get_type(j) == json_type_object) {
211
0
      struct lh_entry* entry = json_object_get_object(j)->head;
212
213
0
      for (; entry != NULL; entry = entry->next) {
214
0
        char* key = (char*)entry->k;
215
0
        struct json_object* val = (struct json_object*)entry->v;
216
0
        enum json_type type = json_object_get_type(val);
217
218
0
        if (type == json_type_int) {
219
0
          u_int32_t value = json_object_get_int64(val);
220
221
#ifdef DEBUG
222
          ntop->getTrace()->traceEvent(TRACE_NORMAL, "%s = %u", key, value);
223
#endif
224
225
0
          incrVisitedData(key, value);
226
0
        }
227
0
      }
228
0
    } else
229
0
      ntop->getTrace()->traceEvent(
230
0
          TRACE_WARNING, "Invalid JSON content for key %s", redis_key_current);
231
232
0
    json_object_put(j); /* Free memory */
233
0
  } else
234
0
    ntop->getTrace()->traceEvent(TRACE_NORMAL, "Deserialization Error: %s",
235
0
                                 json);
236
237
0
  free(json);
238
0
}
239
240
/* *************************************** */
241
242
void MostVisitedList::resetTopSitesData(u_int32_t iface_id, char* extra_info,
243
0
                                        char* hashkey) {
244
0
  char redis_reset_key[256];
245
246
0
  int minute = 0;
247
0
  struct tm t_now;
248
249
0
  if (!ntop->getRedis()) return;
250
251
0
  getCurrentTime(&t_now);
252
0
  minute = t_now.tm_min - (t_now.tm_min % 5);
253
254
0
  snprintf(redis_reset_key, sizeof(redis_reset_key), "%s%u_%d_%u_%d",
255
0
           extra_info, iface_id, t_now.tm_mday, t_now.tm_hour, minute);
256
0
  ntop->getRedis()->lpush(hashkey, redis_reset_key, 0);
257
0
}
258
259
/* *************************************** */