/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 | | /* *************************************** */ |