File: | mod_hash.c |
Warning: | line 560, column 8 Although the value stored to 'item' is used in the enclosing expression, the value is never actually read from 'item' |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* |
2 | * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application |
3 | * Copyright (C) 2005-2014, Anthony Minessale II <anthm@freeswitch.org> |
4 | * |
5 | * Version: MPL 1.1 |
6 | * |
7 | * The contents of this file are subject to the Mozilla Public License Version |
8 | * 1.1 (the "License"); you may not use this file except in compliance with |
9 | * the License. You may obtain a copy of the License at |
10 | * http://www.mozilla.org/MPL/ |
11 | * |
12 | * Software distributed under the License is distributed on an "AS IS" basis, |
13 | * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License |
14 | * for the specific language governing rights and limitations under the |
15 | * License. |
16 | * |
17 | * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application |
18 | * |
19 | * The Initial Developer of the Original Code is |
20 | * Anthony Minessale II <anthm@freeswitch.org> |
21 | * Portions created by the Initial Developer are Copyright (C) |
22 | * the Initial Developer. All Rights Reserved. |
23 | * |
24 | * Contributor(s): |
25 | * |
26 | * Anthony Minessale II <anthm@freeswitch.org> |
27 | * Ken Rice <krice at suspicious dot org |
28 | * Mathieu Rene <mathieu.rene@gmail.com> |
29 | * Bret McDanel <trixter AT 0xdecafbad.com> |
30 | * Rupa Schomaker <rupa@rupa.com> |
31 | * |
32 | * mod_hash.c -- Hash api, hash backend for limit |
33 | * |
34 | */ |
35 | |
36 | #include <switch.h> |
37 | #include "esl.h" |
38 | |
39 | #define LIMIT_HASH_CLEANUP_INTERVAL900 900 |
40 | |
41 | SWITCH_MODULE_LOAD_FUNCTION(mod_hash_load)switch_status_t mod_hash_load (switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool); |
42 | SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_hash_shutdown)switch_status_t mod_hash_shutdown (void); |
43 | SWITCH_MODULE_DEFINITION(mod_hash, mod_hash_load, mod_hash_shutdown, NULL)static const char modname[] = "mod_hash" ; __attribute__((visibility ("default"))) switch_loadable_module_function_table_t mod_hash_module_interface = { 5, mod_hash_load, mod_hash_shutdown, ((void*)0), SMODF_NONE }; |
44 | |
45 | /* CORE STUFF */ |
46 | static struct { |
47 | switch_memory_pool_t *pool; |
48 | switch_thread_rwlock_t *limit_hash_rwlock; |
49 | switch_hash_t *limit_hash; |
50 | switch_thread_rwlock_t *db_hash_rwlock; |
51 | switch_hash_t *db_hash; |
52 | switch_thread_rwlock_t *remote_hash_rwlock; |
53 | switch_hash_t *remote_hash; |
54 | } globals; |
55 | |
56 | typedef struct { |
57 | uint32_t total_usage; /* < Total */ |
58 | uint32_t rate_usage; /* < Current rate usage */ |
59 | time_t last_check; /* < Last rate check */ |
60 | uint32_t interval; /* < Interval used on last rate check */ |
61 | switch_time_t last_update; /* < Last updated timestamp (rate or total) */ |
62 | } limit_hash_item_t; |
63 | |
64 | struct callback { |
65 | char *buf; |
66 | size_t len; |
67 | int matches; |
68 | }; |
69 | |
70 | typedef struct callback callback_t; |
71 | |
72 | /* HASH STUFF */ |
73 | typedef struct { |
74 | switch_hash_t *hash; |
75 | } limit_hash_private_t; |
76 | |
77 | typedef enum { |
78 | REMOTE_OFF = 0, /* < Thread not running */ |
79 | REMOTE_DOWN, /* <C annot connect to remote instance */ |
80 | REMOTE_UP /* < All good */ |
81 | } limit_remote_state_t; |
82 | |
83 | static inline const char *state_str(limit_remote_state_t state) { |
84 | switch (state) { |
85 | case REMOTE_OFF: |
86 | return "Off"; |
87 | case REMOTE_DOWN: |
88 | return "Down"; |
89 | case REMOTE_UP: |
90 | return "Up"; |
91 | } |
92 | return ""; |
93 | } |
94 | |
95 | typedef struct { |
96 | const char *name; |
97 | const char *host; |
98 | const char *username; |
99 | const char *password; |
100 | int port; |
101 | |
102 | int interval; |
103 | |
104 | esl_handle_t handle; |
105 | |
106 | switch_hash_t *index; |
107 | switch_thread_rwlock_t *rwlock; |
108 | switch_memory_pool_t *pool; |
109 | |
110 | switch_bool_t running; |
111 | switch_thread_t *thread; |
112 | |
113 | limit_remote_state_t state; |
114 | } limit_remote_t; |
115 | |
116 | static limit_hash_item_t get_remote_usage(const char *key); |
117 | void limit_remote_destroy(limit_remote_t **r); |
118 | static void do_config(switch_bool_t reload); |
119 | |
120 | |
121 | /* \brief Enforces limit_hash restrictions |
122 | * \param session current session |
123 | * \param realm limit realm |
124 | * \param id limit id |
125 | * \param max maximum count |
126 | * \param interval interval for rate limiting |
127 | * \return SWITCH_TRUE if the access is allowed, SWITCH_FALSE if it isnt |
128 | */ |
129 | SWITCH_LIMIT_INCR(limit_incr_hash)static switch_status_t limit_incr_hash (switch_core_session_t *session, const char *realm, const char *resource, const int max, const int interval) |
130 | { |
131 | switch_channel_t *channel = switch_core_session_get_channel(session); |
132 | char *hashkey = NULL((void*)0); |
133 | switch_status_t status = SWITCH_STATUS_SUCCESS; |
134 | limit_hash_item_t *item = NULL((void*)0); |
135 | time_t now = switch_epoch_time_now(NULL((void*)0)); |
136 | limit_hash_private_t *pvt = NULL((void*)0); |
137 | uint8_t increment = 1; |
138 | limit_hash_item_t remote_usage; |
139 | |
140 | hashkey = switch_core_session_sprintf(session, "%s_%s", realm, resource); |
141 | |
142 | switch_thread_rwlock_wrlock(globals.limit_hash_rwlock); |
143 | /* Check if that realm+resource has ever been checked */ |
144 | if (!(item = (limit_hash_item_t *) switch_core_hash_find(globals.limit_hash, hashkey))) { |
145 | /* No, create an empty structure and add it, then continue like as if it existed */ |
146 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session)SWITCH_CHANNEL_ID_SESSION, "mod_hash.c", (const char *)__func__ , 146, (const char*)switch_core_session_type_check(session), SWITCH_LOG_DEBUG10, "Creating new limit structure: key: %s\n", hashkey); |
147 | item = (limit_hash_item_t *)switch_core_hash_insert_alloc(globals.limit_hash, hashkey, sizeof(limit_hash_item_t))switch_core_hash_insert_alloc_destructor(globals.limit_hash, hashkey , sizeof(limit_hash_item_t), ((void*)0)); |
148 | } |
149 | |
150 | if (!(pvt = switch_channel_get_private(channel, "limit_hash"))) { |
151 | pvt = (limit_hash_private_t *) switch_core_session_alloc(session, sizeof(limit_hash_private_t))switch_core_perform_session_alloc(session, sizeof(limit_hash_private_t ), "mod_hash.c", (const char *)__func__, 151); |
152 | memset(pvt, 0, sizeof(limit_hash_private_t)); |
153 | switch_channel_set_private(channel, "limit_hash", pvt); |
154 | } |
155 | if (!(pvt->hash)) { |
156 | switch_core_hash_init(&pvt->hash)switch_core_hash_init_case(&pvt->hash, SWITCH_TRUE); |
157 | } |
158 | increment = !switch_core_hash_find(pvt->hash, hashkey); |
159 | remote_usage = get_remote_usage(hashkey); |
160 | |
161 | if (interval > 0) { |
162 | item->interval = interval; |
163 | if (item->last_check <= (now - interval)) { |
164 | item->rate_usage = 1; |
165 | item->last_check = now; |
166 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session)SWITCH_CHANNEL_ID_SESSION, "mod_hash.c", (const char *)__func__ , 166, (const char*)switch_core_session_type_check(session), SWITCH_LOG_DEBUG10, "Usage for %s reset to 1\n", |
167 | hashkey); |
168 | } else { |
169 | /* Always increment rate when its checked as it doesnt depend on the channel */ |
170 | item->rate_usage++; |
171 | |
172 | if ((max >= 0) && (item->rate_usage > (uint32_t) max)) { |
173 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session)SWITCH_CHANNEL_ID_SESSION, "mod_hash.c", (const char *)__func__ , 173, (const char*)switch_core_session_type_check(session), SWITCH_LOG_INFO, "Usage for %s exceeds maximum rate of %d/%ds, now at %d\n", |
174 | hashkey, max, interval, item->rate_usage); |
175 | status = SWITCH_STATUS_GENERR; |
176 | goto end; |
177 | } |
178 | } |
179 | } else if ((max >= 0) && (item->total_usage + increment + remote_usage.total_usage > (uint32_t) max)) { |
180 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session)SWITCH_CHANNEL_ID_SESSION, "mod_hash.c", (const char *)__func__ , 180, (const char*)switch_core_session_type_check(session), SWITCH_LOG_INFO, "Usage for %s is already at max value (%d)\n", hashkey, item->total_usage); |
181 | status = SWITCH_STATUS_GENERR; |
182 | goto end; |
183 | } |
184 | |
185 | if (increment) { |
186 | item->total_usage++; |
187 | |
188 | switch_core_hash_insert(pvt->hash, hashkey, item)switch_core_hash_insert_destructor(pvt->hash, hashkey, item , ((void*)0)); |
189 | |
190 | if (max == -1) { |
191 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session)SWITCH_CHANNEL_ID_SESSION, "mod_hash.c", (const char *)__func__ , 191, (const char*)switch_core_session_type_check(session), SWITCH_LOG_DEBUG, "Usage for %s is now %d\n", hashkey, item->total_usage + remote_usage.total_usage); |
192 | } else if (interval == 0) { |
193 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session)SWITCH_CHANNEL_ID_SESSION, "mod_hash.c", (const char *)__func__ , 193, (const char*)switch_core_session_type_check(session), SWITCH_LOG_DEBUG, "Usage for %s is now %d/%d\n", hashkey, item->total_usage + remote_usage.total_usage, max); |
194 | } else { |
195 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session)SWITCH_CHANNEL_ID_SESSION, "mod_hash.c", (const char *)__func__ , 195, (const char*)switch_core_session_type_check(session), SWITCH_LOG_DEBUG, "Usage for %s is now %d/%d for the last %d seconds\n", hashkey, |
196 | item->rate_usage, max, interval); |
197 | } |
198 | |
199 | switch_limit_fire_event("hash", realm, resource, item->total_usage, item->rate_usage, max, max >= 0 ? (uint32_t) max : 0); |
200 | } |
201 | |
202 | /* Save current usage & rate into channel variables so it can be used later in the dialplan, or added to CDR records */ |
203 | { |
204 | const char *susage = switch_core_session_sprintf(session, "%d", item->total_usage); |
205 | const char *srate = switch_core_session_sprintf(session, "%d", item->rate_usage); |
206 | |
207 | switch_channel_set_variable(channel, "limit_usage", susage)switch_channel_set_variable_var_check(channel, "limit_usage", susage, SWITCH_TRUE); |
208 | switch_channel_set_variable(channel, switch_core_session_sprintf(session, "limit_usage_%s", hashkey), susage)switch_channel_set_variable_var_check(channel, switch_core_session_sprintf (session, "limit_usage_%s", hashkey), susage, SWITCH_TRUE); |
209 | |
210 | switch_channel_set_variable(channel, "limit_rate", srate)switch_channel_set_variable_var_check(channel, "limit_rate", srate , SWITCH_TRUE); |
211 | switch_channel_set_variable(channel, switch_core_session_sprintf(session, "limit_rate_%s", hashkey), srate)switch_channel_set_variable_var_check(channel, switch_core_session_sprintf (session, "limit_rate_%s", hashkey), srate, SWITCH_TRUE); |
212 | } |
213 | |
214 | end: |
215 | switch_thread_rwlock_unlock(globals.limit_hash_rwlock); |
216 | return status; |
217 | } |
218 | |
219 | /* !\brief Determines whether a given entry is ready to be removed. */ |
220 | SWITCH_HASH_DELETE_FUNC(limit_hash_cleanup_delete_callback)static switch_bool_t limit_hash_cleanup_delete_callback (const void *key, const void *val, void *pData) { |
221 | limit_hash_item_t *item = (limit_hash_item_t *) val; |
222 | time_t now = switch_epoch_time_now(NULL((void*)0)); |
223 | |
224 | /* reset to 0 if window has passed so we can clean it up */ |
225 | if (item->rate_usage > 0 && (item->last_check <= (now - item->interval))) { |
226 | item->rate_usage = 0; |
227 | } |
228 | |
229 | if (item->total_usage == 0 && item->rate_usage == 0) { |
230 | /* Noone is using this item anymore */ |
231 | switch_log_printf(SWITCH_CHANNEL_LOGSWITCH_CHANNEL_ID_LOG, "mod_hash.c", (const char *)__func__, 231 , ((void*)0), SWITCH_LOG_DEBUG, "Freeing limit item: %s\n", (const char *) key); |
232 | |
233 | free(item); |
234 | return SWITCH_TRUE; |
235 | } |
236 | |
237 | return SWITCH_FALSE; |
238 | } |
239 | |
240 | SWITCH_HASH_DELETE_FUNC(limit_hash_remote_cleanup_callback)static switch_bool_t limit_hash_remote_cleanup_callback (const void *key, const void *val, void *pData) |
241 | { |
242 | limit_hash_item_t *item = (limit_hash_item_t *) val; |
243 | switch_time_t now = (switch_time_t)(intptr_t)pData; |
244 | |
245 | if (item->last_update != now) { |
246 | return SWITCH_TRUE; |
247 | } |
248 | |
249 | return SWITCH_FALSE; |
250 | } |
251 | |
252 | /* !\brief Periodically checks for unused limit entries and frees them */ |
253 | SWITCH_STANDARD_SCHED_FUNC(limit_hash_cleanup_callback)static void limit_hash_cleanup_callback (switch_scheduler_task_t *task) |
254 | { |
255 | switch_thread_rwlock_wrlock(globals.limit_hash_rwlock); |
256 | if (globals.limit_hash) { |
257 | switch_core_hash_delete_multi(globals.limit_hash, limit_hash_cleanup_delete_callback, NULL((void*)0)); |
258 | } |
259 | switch_thread_rwlock_unlock(globals.limit_hash_rwlock); |
260 | |
261 | if (globals.limit_hash) { |
262 | task->runtime = switch_epoch_time_now(NULL((void*)0)) + LIMIT_HASH_CLEANUP_INTERVAL900; |
263 | } |
264 | } |
265 | |
266 | /* !\brief Releases usage of a limit_hash-controlled resource */ |
267 | SWITCH_LIMIT_RELEASE(limit_release_hash)static switch_status_t limit_release_hash (switch_core_session_t *session, const char *realm, const char *resource) |
268 | { |
269 | switch_channel_t *channel = switch_core_session_get_channel(session); |
270 | limit_hash_private_t *pvt = switch_channel_get_private(channel, "limit_hash"); |
271 | limit_hash_item_t *item = NULL((void*)0); |
272 | |
273 | switch_thread_rwlock_wrlock(globals.limit_hash_rwlock); |
274 | |
275 | if (!pvt || !pvt->hash) { |
276 | switch_thread_rwlock_unlock(globals.limit_hash_rwlock); |
277 | return SWITCH_STATUS_SUCCESS; |
278 | } |
279 | |
280 | /* clear for uuid */ |
281 | if (realm == NULL((void*)0) && resource == NULL((void*)0)) { |
282 | switch_hash_index_t *hi = NULL((void*)0); |
283 | /* Loop through the channel's hashtable which contains mapping to all the limit_hash_item_t referenced by that channel */ |
284 | while ((hi = switch_core_hash_first_iter(pvt->hash, hi))) { |
285 | void *val = NULL((void*)0); |
286 | const void *key; |
287 | switch_ssize_t keylen; |
288 | |
289 | switch_core_hash_this(hi, &key, &keylen, &val); |
290 | |
291 | item = (limit_hash_item_t *) val; |
292 | item->total_usage--; |
293 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session)SWITCH_CHANNEL_ID_SESSION, "mod_hash.c", (const char *)__func__ , 293, (const char*)switch_core_session_type_check(session), SWITCH_LOG_DEBUG, "Usage for %s is now %d\n", (const char *) key, item->total_usage); |
294 | |
295 | if (item->total_usage == 0 && item->rate_usage == 0) { |
296 | /* Noone is using this item anymore */ |
297 | switch_core_hash_delete(globals.limit_hash, (const char *) key); |
298 | free(item); |
299 | } |
300 | |
301 | switch_core_hash_delete(pvt->hash, (const char *) key); |
302 | } |
303 | switch_core_hash_destroy(&pvt->hash); |
304 | } else { |
305 | char *hashkey = switch_core_session_sprintf(session, "%s_%s", realm, resource); |
306 | |
307 | if ((item = (limit_hash_item_t *) switch_core_hash_find(pvt->hash, hashkey))) { |
308 | item->total_usage--; |
309 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session)SWITCH_CHANNEL_ID_SESSION, "mod_hash.c", (const char *)__func__ , 309, (const char*)switch_core_session_type_check(session), SWITCH_LOG_DEBUG, "Usage for %s is now %d\n", (const char *) hashkey, item->total_usage); |
310 | |
311 | switch_core_hash_delete(pvt->hash, hashkey); |
312 | |
313 | if (item->total_usage == 0 && item->rate_usage == 0) { |
314 | /* Noone is using this item anymore */ |
315 | switch_core_hash_delete(globals.limit_hash, (const char *) hashkey); |
316 | free(item); |
317 | } |
318 | } |
319 | } |
320 | |
321 | switch_thread_rwlock_unlock(globals.limit_hash_rwlock); |
322 | |
323 | return SWITCH_STATUS_SUCCESS; |
324 | } |
325 | |
326 | SWITCH_LIMIT_USAGE(limit_usage_hash)static int limit_usage_hash (const char *realm, const char *resource , uint32_t *rcount) |
327 | { |
328 | char *hash_key = NULL((void*)0); |
329 | limit_hash_item_t *item = NULL((void*)0); |
330 | int count = 0; |
331 | limit_hash_item_t remote_usage; |
332 | |
333 | switch_thread_rwlock_rdlock(globals.limit_hash_rwlock); |
334 | |
335 | hash_key = switch_mprintf("%s_%s", realm, resource); |
336 | remote_usage = get_remote_usage(hash_key); |
337 | |
338 | count = remote_usage.total_usage; |
339 | *rcount = remote_usage.rate_usage; |
340 | |
341 | if ((item = switch_core_hash_find(globals.limit_hash, hash_key))) { |
342 | count += item->total_usage; |
343 | *rcount += item->rate_usage; |
344 | } |
345 | |
346 | switch_safe_free(hash_key)if (hash_key) {free(hash_key);hash_key=((void*)0);}; |
347 | switch_thread_rwlock_unlock(globals.limit_hash_rwlock); |
348 | |
349 | return count; |
350 | } |
351 | |
352 | SWITCH_LIMIT_RESET(limit_reset_hash)static switch_status_t limit_reset_hash (void) |
353 | { |
354 | return SWITCH_STATUS_GENERR; |
355 | } |
356 | |
357 | SWITCH_LIMIT_INTERVAL_RESET(limit_interval_reset_hash)static switch_status_t limit_interval_reset_hash (const char * realm, const char *resource) |
358 | { |
359 | char *hash_key = NULL((void*)0); |
360 | limit_hash_item_t *item = NULL((void*)0); |
361 | |
362 | switch_thread_rwlock_rdlock(globals.limit_hash_rwlock); |
363 | |
364 | hash_key = switch_mprintf("%s_%s", realm, resource); |
365 | if ((item = switch_core_hash_find(globals.limit_hash, hash_key))) { |
366 | item->rate_usage = 0; |
367 | item->last_check = switch_epoch_time_now(NULL((void*)0)); |
368 | } |
369 | |
370 | switch_safe_free(hash_key)if (hash_key) {free(hash_key);hash_key=((void*)0);}; |
371 | switch_thread_rwlock_unlock(globals.limit_hash_rwlock); |
372 | return SWITCH_STATUS_SUCCESS; |
373 | } |
374 | |
375 | SWITCH_LIMIT_STATUS(limit_status_hash)static char * limit_status_hash (void) |
376 | { |
377 | /* |
378 | switch_hash_index_t *hi = NULL; |
379 | int count = 0; |
380 | char *ret = NULL; |
381 | |
382 | switch_thread_rwlock_rdlock(globals.limit_hash_rwlock); |
383 | |
384 | for (hi = switch_core_hash_first(globals.limit_hash); hi; switch_core_hash_next(hi)) { |
385 | count++; |
386 | } |
387 | |
388 | switch_thread_rwlock_unlock(globals.limit_hash_rwlock); |
389 | |
390 | ret = switch_mprintf("There are %d elements being tracked.", count); |
391 | return ret; |
392 | */ |
393 | return strdup("-ERR not supported yet (locking problems)."); |
394 | } |
395 | |
396 | /* APP/API STUFF */ |
397 | |
398 | /* CORE HASH STUFF */ |
399 | |
400 | #define HASH_USAGE"[insert|insert_ifempty|delete|delete_ifmatch]/<realm>/<key>/<val>" "[insert|insert_ifempty|delete|delete_ifmatch]/<realm>/<key>/<val>" |
401 | #define HASH_DESC"save data" "save data" |
402 | |
403 | SWITCH_STANDARD_APP(hash_function)static void hash_function (switch_core_session_t *session, const char *data) |
404 | { |
405 | int argc = 0; |
406 | char *argv[4] = { 0 }; |
407 | char *mydata = NULL((void*)0); |
408 | char *hash_key = NULL((void*)0); |
409 | char *value = NULL((void*)0); |
410 | |
411 | switch_thread_rwlock_wrlock(globals.db_hash_rwlock); |
412 | |
413 | if (!zstr(data)_zstr(data)) { |
414 | mydata = strdup(data); |
415 | switch_assert(mydata)((mydata) ? (void) (0) : __assert_fail ("mydata", "mod_hash.c" , 415, __extension__ __PRETTY_FUNCTION__)); |
416 | argc = switch_separate_string(mydata, '/', argv, (sizeof(argv) / sizeof(argv[0]))); |
417 | } |
418 | |
419 | if (argc < 3 || !argv[0]) { |
420 | goto usage; |
421 | } |
422 | |
423 | hash_key = switch_mprintf("%s_%s", argv[1], argv[2]); |
424 | |
425 | if (!strcasecmp(argv[0], "insert")) { |
426 | if (argc < 4) { |
427 | goto usage; |
428 | } |
429 | if ((value = switch_core_hash_find(globals.db_hash, hash_key))) { |
430 | free(value); |
431 | switch_core_hash_delete(globals.db_hash, hash_key); |
432 | } |
433 | switch_core_hash_insert_dup(globals.db_hash, hash_key, argv[3])switch_core_hash_insert_dup_destructor(globals.db_hash, hash_key , argv[3], ((void*)0)); |
434 | } else if (!strcasecmp(argv[0], "insert_ifempty")) { |
435 | if (argc < 4) { |
436 | goto usage; |
437 | } |
438 | if (!switch_core_hash_find(globals.db_hash, hash_key)) { |
439 | switch_core_hash_insert_dup(globals.db_hash, hash_key, argv[3])switch_core_hash_insert_dup_destructor(globals.db_hash, hash_key , argv[3], ((void*)0)); |
440 | } |
441 | |
442 | } else if (!strcasecmp(argv[0], "delete")) { |
443 | if ((value = switch_core_hash_find(globals.db_hash, hash_key))) { |
444 | switch_safe_free(value)if (value) {free(value);value=((void*)0);}; |
445 | switch_core_hash_delete(globals.db_hash, hash_key); |
446 | } |
447 | } else if (!strcasecmp(argv[0], "delete_ifmatch")) { |
448 | if (argc < 4) { |
449 | goto usage; |
450 | } |
451 | if ((value = switch_core_hash_find(globals.db_hash, hash_key))) { |
452 | if(!strcmp(argv[3], value)) { |
453 | switch_safe_free(value)if (value) {free(value);value=((void*)0);}; |
454 | switch_core_hash_delete(globals.db_hash, hash_key); |
455 | } |
456 | } |
457 | } else { |
458 | goto usage; |
459 | } |
460 | |
461 | goto done; |
462 | |
463 | usage: |
464 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session)SWITCH_CHANNEL_ID_SESSION, "mod_hash.c", (const char *)__func__ , 464, (const char*)switch_core_session_type_check(session), SWITCH_LOG_WARNING, "USAGE: hash %s\n", HASH_USAGE"[insert|insert_ifempty|delete|delete_ifmatch]/<realm>/<key>/<val>"); |
465 | |
466 | done: |
467 | switch_thread_rwlock_unlock(globals.db_hash_rwlock); |
468 | switch_safe_free(mydata)if (mydata) {free(mydata);mydata=((void*)0);}; |
469 | switch_safe_free(hash_key)if (hash_key) {free(hash_key);hash_key=((void*)0);}; |
470 | } |
471 | |
472 | #define HASH_API_USAGE"insert|insert_ifempty|select|delete|delete_ifmatch|select_limit|delete_limit/realm/key[/value]" "insert|insert_ifempty|select|delete|delete_ifmatch|select_limit|delete_limit/realm/key[/value]" |
473 | SWITCH_STANDARD_API(hash_api_function)static switch_status_t hash_api_function ( const char *cmd, switch_core_session_t *session, switch_stream_handle_t *stream) |
474 | { |
475 | int argc = 0; |
476 | char *argv[4] = { 0 }; |
477 | char *mydata = NULL((void*)0); |
478 | char *value = NULL((void*)0); |
479 | char *hash_key = NULL((void*)0); |
480 | |
481 | if (!zstr(cmd)_zstr(cmd)) { |
482 | mydata = strdup(cmd); |
483 | switch_assert(mydata)((mydata) ? (void) (0) : __assert_fail ("mydata", "mod_hash.c" , 483, __extension__ __PRETTY_FUNCTION__)); |
484 | argc = switch_separate_string(mydata, '/', argv, (sizeof(argv) / sizeof(argv[0]))); |
485 | } |
486 | |
487 | if (argc < 3 || !argv[0]) { |
488 | goto usage; |
489 | } |
490 | |
491 | hash_key = switch_mprintf("%s_%s", argv[1], argv[2]); |
492 | |
493 | if (!strcasecmp(argv[0], "insert")) { |
494 | if (argc < 4) { |
495 | goto usage; |
496 | } |
497 | switch_thread_rwlock_wrlock(globals.db_hash_rwlock); |
498 | if ((value = switch_core_hash_find(globals.db_hash, hash_key))) { |
499 | switch_safe_free(value)if (value) {free(value);value=((void*)0);}; |
500 | switch_core_hash_delete(globals.db_hash, hash_key); |
501 | } |
502 | switch_core_hash_insert_dup(globals.db_hash, hash_key, argv[3])switch_core_hash_insert_dup_destructor(globals.db_hash, hash_key , argv[3], ((void*)0)); |
503 | stream->write_function(stream, "+OK\n"); |
504 | switch_thread_rwlock_unlock(globals.db_hash_rwlock); |
505 | } else if (!strcasecmp(argv[0], "insert_ifempty")) { |
506 | if (argc < 4) { |
507 | goto usage; |
508 | } |
509 | switch_thread_rwlock_wrlock(globals.db_hash_rwlock); |
510 | if (switch_core_hash_find(globals.db_hash, hash_key)) { |
511 | stream->write_function(stream, "-ERR key already exists\n"); |
512 | } else { |
513 | switch_core_hash_insert_dup(globals.db_hash, hash_key, argv[3])switch_core_hash_insert_dup_destructor(globals.db_hash, hash_key , argv[3], ((void*)0)); |
514 | stream->write_function(stream, "+OK\n"); |
515 | } |
516 | switch_thread_rwlock_unlock(globals.db_hash_rwlock); |
517 | } else if (!strcasecmp(argv[0], "delete")) { |
518 | switch_thread_rwlock_wrlock(globals.db_hash_rwlock); |
519 | if ((value = switch_core_hash_find(globals.db_hash, hash_key))) { |
520 | switch_safe_free(value)if (value) {free(value);value=((void*)0);}; |
521 | switch_core_hash_delete(globals.db_hash, hash_key); |
522 | stream->write_function(stream, "+OK\n"); |
523 | } else { |
524 | stream->write_function(stream, "-ERR Not found\n"); |
525 | } |
526 | switch_thread_rwlock_unlock(globals.db_hash_rwlock); |
527 | } else if (!strcasecmp(argv[0], "delete_ifmatch")) { |
528 | if (argc < 4) { |
529 | goto usage; |
530 | } |
531 | switch_thread_rwlock_wrlock(globals.db_hash_rwlock); |
532 | if ((value = switch_core_hash_find(globals.db_hash, hash_key))) { |
533 | if(!strcmp(argv[3],value)) { |
534 | switch_safe_free(value)if (value) {free(value);value=((void*)0);}; |
535 | switch_core_hash_delete(globals.db_hash, hash_key); |
536 | stream->write_function(stream, "+OK\n"); |
537 | } else { |
538 | stream->write_function(stream, "-ERR Doesn't match\n"); |
539 | } |
540 | } else { |
541 | stream->write_function(stream, "-ERR Not found\n"); |
542 | } |
543 | switch_thread_rwlock_unlock(globals.db_hash_rwlock); |
544 | } else if (!strcasecmp(argv[0], "select")) { |
545 | switch_thread_rwlock_rdlock(globals.db_hash_rwlock); |
546 | if ((value = switch_core_hash_find(globals.db_hash, hash_key))) { |
547 | stream->write_function(stream, "%s", value); |
548 | } |
549 | switch_thread_rwlock_unlock(globals.db_hash_rwlock); |
550 | } else if (!strcasecmp(argv[0], "select_limit")) { |
551 | limit_hash_item_t *item = NULL((void*)0); |
552 | switch_thread_rwlock_rdlock(globals.limit_hash_rwlock); |
553 | if ((item = switch_core_hash_find(globals.limit_hash, hash_key))) { |
554 | stream->write_function(stream, "%d", item->total_usage); |
555 | } |
556 | switch_thread_rwlock_unlock(globals.limit_hash_rwlock); |
557 | } else if (!strcasecmp(argv[0], "delete_limit")) { |
558 | limit_hash_item_t *item = NULL((void*)0); |
559 | switch_thread_rwlock_rdlock(globals.limit_hash_rwlock); |
560 | if ((item = switch_core_hash_find(globals.limit_hash, hash_key))) { |
Although the value stored to 'item' is used in the enclosing expression, the value is never actually read from 'item' | |
561 | switch_core_hash_delete(globals.limit_hash, hash_key); |
562 | stream->write_function(stream, "+OK\n"); |
563 | } else { |
564 | stream->write_function(stream, "-ERR Not found\n"); |
565 | } |
566 | switch_thread_rwlock_unlock(globals.limit_hash_rwlock); |
567 | } else { |
568 | goto usage; |
569 | } |
570 | |
571 | goto done; |
572 | |
573 | usage: |
574 | stream->write_function(stream, "-ERR Usage: hash %s\n", HASH_API_USAGE"insert|insert_ifempty|select|delete|delete_ifmatch|select_limit|delete_limit/realm/key[/value]"); |
575 | |
576 | done: |
577 | |
578 | switch_safe_free(mydata)if (mydata) {free(mydata);mydata=((void*)0);}; |
579 | switch_safe_free(hash_key)if (hash_key) {free(hash_key);hash_key=((void*)0);}; |
580 | |
581 | return SWITCH_STATUS_SUCCESS; |
582 | } |
583 | |
584 | #define HASH_DUMP_SYNTAX"all|limit|db [<realm>]" "all|limit|db [<realm>]" |
585 | SWITCH_STANDARD_API(hash_dump_function)static switch_status_t hash_dump_function ( const char *cmd, switch_core_session_t *session, switch_stream_handle_t *stream) |
586 | { |
587 | int mode; |
588 | switch_hash_index_t *hi; |
589 | int argc = 0; |
590 | char *argv[4] = { 0 }; |
591 | char *mydata = NULL((void*)0); |
592 | int realm = 0; |
593 | char *realmvalue = NULL((void*)0); |
594 | |
595 | if (zstr(cmd)_zstr(cmd)) { |
596 | stream->write_function(stream, "Usage: "HASH_DUMP_SYNTAX"all|limit|db [<realm>]""\n"); |
597 | goto done; |
598 | } |
599 | |
600 | mydata = strdup(cmd); |
601 | switch_assert(mydata)((mydata) ? (void) (0) : __assert_fail ("mydata", "mod_hash.c" , 601, __extension__ __PRETTY_FUNCTION__)); |
602 | argc = switch_separate_string(mydata, ' ', argv, (sizeof(argv) / sizeof(argv[0]))); |
603 | cmd = argv[0]; |
604 | |
605 | if (argc == 2) { |
606 | realm = 1; |
607 | realmvalue = switch_mprintf("%s_", argv[1]); |
608 | } |
609 | |
610 | if (!strcmp(cmd, "all")) { |
611 | mode = 3; |
612 | } else if (!strcmp(cmd, "limit")) { |
613 | mode = 1; |
614 | } else if (!strcmp(cmd, "db")) { |
615 | mode = 2; |
616 | } else { |
617 | stream->write_function(stream, "Usage: "HASH_DUMP_SYNTAX"all|limit|db [<realm>]""\n"); |
618 | goto done; |
619 | } |
620 | |
621 | if (mode & 1) { |
622 | switch_thread_rwlock_rdlock(globals.limit_hash_rwlock); |
623 | for (hi = switch_core_hash_first(globals.limit_hash)switch_core_hash_first_iter(globals.limit_hash, ((void*)0)); hi; hi = switch_core_hash_next(&hi)) { |
624 | void *val = NULL((void*)0); |
625 | const void *key; |
626 | switch_ssize_t keylen; |
627 | limit_hash_item_t *item; |
628 | switch_core_hash_this(hi, &key, &keylen, &val); |
629 | |
630 | item = (limit_hash_item_t *)val; |
631 | |
632 | stream->write_function(stream, "L/%s/%d/%d/%d/%d\n", key, item->total_usage, item->rate_usage, item->interval, item->last_check); |
633 | } |
634 | switch_thread_rwlock_unlock(globals.limit_hash_rwlock); |
635 | } |
636 | |
637 | if (mode & 2) { |
638 | switch_thread_rwlock_rdlock(globals.db_hash_rwlock); |
639 | for (hi = switch_core_hash_first(globals.db_hash)switch_core_hash_first_iter(globals.db_hash, ((void*)0)); hi; hi = switch_core_hash_next(&hi)) { |
640 | void *val = NULL((void*)0); |
641 | const void *key; |
642 | switch_ssize_t keylen; |
643 | switch_core_hash_this(hi, &key, &keylen, &val); |
644 | if (realm) { |
645 | if (strstr(key, realmvalue)) { |
646 | stream->write_function(stream, "D/%s/%s\n", key, (char*)val); |
647 | } |
648 | } else { |
649 | stream->write_function(stream, "D/%s/%s\n", key, (char*)val); |
650 | } |
651 | } |
652 | switch_thread_rwlock_unlock(globals.db_hash_rwlock); |
653 | } |
654 | |
655 | done: |
656 | switch_safe_free(mydata)if (mydata) {free(mydata);mydata=((void*)0);}; |
657 | switch_safe_free(realmvalue)if (realmvalue) {free(realmvalue);realmvalue=((void*)0);}; |
658 | |
659 | return SWITCH_STATUS_SUCCESS; |
660 | } |
661 | |
662 | #define HASH_REMOTE_SYNTAX"list|kill [name]|rescan" "list|kill [name]|rescan" |
663 | SWITCH_STANDARD_API(hash_remote_function)static switch_status_t hash_remote_function ( const char *cmd , switch_core_session_t *session, switch_stream_handle_t *stream ) |
664 | { |
665 | //int argc; |
666 | char *argv[10]; |
667 | char *dup = NULL((void*)0); |
668 | |
669 | if (zstr(cmd)_zstr(cmd)) { |
670 | stream->write_function(stream, "-ERR Usage: "HASH_REMOTE_SYNTAX"list|kill [name]|rescan""\n"); |
671 | return SWITCH_STATUS_SUCCESS; |
672 | } |
673 | |
674 | dup = strdup(cmd); |
675 | |
676 | switch_split(dup, ' ', argv)switch_separate_string(dup, ' ', argv, (sizeof(argv) / sizeof (argv[0]))); |
677 | if (argv[0] && !strcmp(argv[0], "list")) { |
678 | switch_hash_index_t *hi; |
679 | stream->write_function(stream, "Remote connections:\nName\t\t\tState\n"); |
680 | |
681 | switch_thread_rwlock_rdlock(globals.remote_hash_rwlock); |
682 | for (hi = switch_core_hash_first(globals.remote_hash)switch_core_hash_first_iter(globals.remote_hash, ((void*)0)); hi; hi = switch_core_hash_next(&hi)) { |
683 | void *val; |
684 | const void *key; |
685 | switch_ssize_t keylen; |
686 | limit_remote_t *item; |
687 | switch_core_hash_this(hi, &key, &keylen, &val); |
688 | |
689 | item = (limit_remote_t *)val; |
690 | stream->write_function(stream, "%s\t\t\t%s\n", item->name, state_str(item->state)); |
691 | } |
692 | switch_thread_rwlock_unlock(globals.remote_hash_rwlock); |
693 | stream->write_function(stream, "+OK\n"); |
694 | |
695 | } else if (argv[0] && !strcmp(argv[0], "kill")) { |
696 | const char *name = argv[1]; |
697 | limit_remote_t *remote; |
698 | if (zstr(name)_zstr(name)) { |
699 | stream->write_function(stream, "-ERR Usage: "HASH_REMOTE_SYNTAX"list|kill [name]|rescan""\n"); |
700 | goto done; |
701 | } |
702 | switch_thread_rwlock_rdlock(globals.remote_hash_rwlock); |
703 | remote = switch_core_hash_find(globals.remote_hash, name); |
704 | switch_thread_rwlock_unlock(globals.remote_hash_rwlock); |
705 | |
706 | if (remote) { |
707 | limit_remote_destroy(&remote); |
708 | |
709 | switch_thread_rwlock_wrlock(globals.remote_hash_rwlock); |
710 | switch_core_hash_delete(globals.remote_hash, name); |
711 | switch_thread_rwlock_unlock(globals.remote_hash_rwlock); |
712 | |
713 | stream->write_function(stream, "+OK\n"); |
714 | } else { |
715 | stream->write_function(stream, "-ERR No such remote instance %s\n", name); |
716 | } |
717 | } else if (argv[0] && !strcmp(argv[0], "rescan")) { |
718 | do_config(SWITCH_TRUE); |
719 | stream->write_function(stream, "+OK\n"); |
720 | } else { |
721 | stream->write_function(stream, "-ERR Usage: "HASH_REMOTE_SYNTAX"list|kill [name]|rescan""\n"); |
722 | |
723 | } |
724 | |
725 | done: |
726 | |
727 | switch_safe_free(dup)if (dup) {free(dup);dup=((void*)0);}; |
728 | |
729 | return SWITCH_STATUS_SUCCESS; |
730 | } |
731 | |
732 | limit_remote_t *limit_remote_create(const char *name, const char *host, uint16_t port, const char *username, const char *password, int interval) |
733 | { |
734 | limit_remote_t *r; |
735 | switch_memory_pool_t *pool; |
736 | |
737 | switch_thread_rwlock_rdlock(globals.remote_hash_rwlock); |
738 | if (switch_core_hash_find(globals.remote_hash, name)) { |
739 | switch_log_printf(SWITCH_CHANNEL_LOGSWITCH_CHANNEL_ID_LOG, "mod_hash.c", (const char *)__func__, 739 , ((void*)0), SWITCH_LOG_ERROR, "Already have a remote instance named %s\n", name); |
740 | switch_thread_rwlock_unlock(globals.remote_hash_rwlock); |
741 | return NULL((void*)0); |
742 | } |
743 | switch_thread_rwlock_unlock(globals.remote_hash_rwlock); |
744 | |
745 | if (switch_core_new_memory_pool(&pool)switch_core_perform_new_memory_pool(&pool, "mod_hash.c", ( const char *)__func__, 745) != SWITCH_STATUS_SUCCESS) { |
746 | return NULL((void*)0); |
747 | } |
748 | |
749 | r = switch_core_alloc(pool, sizeof(limit_remote_t))switch_core_perform_alloc(pool, sizeof(limit_remote_t), "mod_hash.c" , (const char *)__func__, 749); |
750 | r->pool = pool; |
751 | r->name = switch_core_strdup(r->pool, name)switch_core_perform_strdup(r->pool, name, "mod_hash.c", (const char *)__func__, 751); |
752 | r->host = switch_core_strdup(r->pool, host)switch_core_perform_strdup(r->pool, host, "mod_hash.c", (const char *)__func__, 752); |
753 | r->port = port; |
754 | r->username = switch_core_strdup(r->pool, username)switch_core_perform_strdup(r->pool, username, "mod_hash.c" , (const char *)__func__, 754); |
755 | r->password = switch_core_strdup(r->pool, password)switch_core_perform_strdup(r->pool, password, "mod_hash.c" , (const char *)__func__, 755); |
756 | r->interval = interval; |
757 | |
758 | switch_thread_rwlock_create(&r->rwlock, pool); |
759 | switch_core_hash_init(&r->index)switch_core_hash_init_case(&r->index, SWITCH_TRUE); |
760 | |
761 | switch_thread_rwlock_rdlock(globals.remote_hash_rwlock); |
762 | switch_core_hash_insert(globals.remote_hash, name, r)switch_core_hash_insert_destructor(globals.remote_hash, name, r, ((void*)0)); |
763 | switch_thread_rwlock_unlock(globals.remote_hash_rwlock); |
764 | |
765 | return r; |
766 | } |
767 | |
768 | void limit_remote_destroy(limit_remote_t **r) |
769 | { |
770 | if (r && *r) { |
771 | (*r)->state = REMOTE_OFF; |
772 | |
773 | if ((*r)->thread) { |
774 | switch_status_t retval; |
775 | switch_thread_join(&retval, (*r)->thread); |
776 | } |
777 | |
778 | switch_thread_rwlock_wrlock((*r)->rwlock); |
779 | |
780 | /* Free hashtable data */ |
781 | switch_core_hash_destroy(&(*r)->index); |
782 | |
783 | switch_thread_rwlock_unlock((*r)->rwlock); |
784 | switch_thread_rwlock_destroy((*r)->rwlock); |
785 | |
786 | switch_core_destroy_memory_pool(&((*r)->pool))switch_core_perform_destroy_memory_pool(&((*r)->pool), "mod_hash.c", (const char *)__func__, 786); |
787 | *r = NULL((void*)0); |
788 | } |
789 | } |
790 | |
791 | /* Compute the usage sum of a resource on remote boxes */ |
792 | static limit_hash_item_t get_remote_usage(const char *key) { |
793 | limit_hash_item_t usage = { 0 }; |
794 | switch_hash_index_t *hi; |
795 | |
796 | switch_thread_rwlock_rdlock(globals.remote_hash_rwlock); |
797 | for (hi = switch_core_hash_first(globals.remote_hash)switch_core_hash_first_iter(globals.remote_hash, ((void*)0)); hi; hi = switch_core_hash_next(&hi)) { |
798 | void *val; |
799 | const void *hashkey; |
800 | switch_ssize_t keylen; |
801 | limit_remote_t *remote; |
802 | limit_hash_item_t *item; |
803 | switch_core_hash_this(hi, &hashkey, &keylen, &val); |
804 | |
805 | remote = (limit_remote_t *)val; |
806 | if (remote->state != REMOTE_UP) { |
807 | continue; |
808 | } |
809 | |
810 | switch_thread_rwlock_rdlock(remote->rwlock); |
811 | if ((item = switch_core_hash_find(remote->index, key))) { |
812 | usage.total_usage += item->total_usage; |
813 | usage.rate_usage += item->rate_usage; |
814 | if (!usage.last_check) { |
815 | usage.last_check = item->last_check; |
816 | } |
817 | } |
818 | switch_thread_rwlock_unlock(remote->rwlock); |
819 | } |
820 | |
821 | switch_thread_rwlock_unlock(globals.remote_hash_rwlock); |
822 | |
823 | return usage; |
824 | } |
825 | |
826 | static void *SWITCH_THREAD_FUNC limit_remote_thread(switch_thread_t *thread, void *obj) |
827 | { |
828 | limit_remote_t *remote = (limit_remote_t*)obj; |
829 | while (remote->state > REMOTE_OFF) { |
830 | if (remote->state != REMOTE_UP) { |
831 | if (esl_connect_timeout(&remote->handle, remote->host, (esl_port_t)remote->port, remote->username, remote->password, 5000) == ESL_SUCCESS) { |
832 | switch_log_printf(SWITCH_CHANNEL_LOGSWITCH_CHANNEL_ID_LOG, "mod_hash.c", (const char *)__func__, 832 , ((void*)0), SWITCH_LOG_INFO, "Connected to remote FreeSWITCH (%s) at %s:%d\n", |
833 | remote->name, remote->host, remote->port); |
834 | |
835 | remote->state = REMOTE_UP; |
836 | } else { |
837 | esl_disconnect(&remote->handle); |
838 | memset(&remote->handle, 0, sizeof(remote->handle)); |
839 | } |
840 | } else { |
841 | if (esl_send_recv_timed(&remote->handle, "api hash_dump limit", 5000) != ESL_SUCCESS) { |
842 | esl_disconnect(&remote->handle); |
843 | memset(&remote->handle, 0, sizeof(remote->handle)); |
844 | switch_log_printf(SWITCH_CHANNEL_LOGSWITCH_CHANNEL_ID_LOG, "mod_hash.c", (const char *)__func__, 844 , ((void*)0), SWITCH_LOG_WARNING, "Disconnected from remote FreeSWITCH (%s) at %s:%d\n", |
845 | remote->name, remote->host, remote->port); |
846 | memset(&remote->handle, 0, sizeof(remote->handle)); |
847 | remote->state = REMOTE_DOWN; |
848 | /* Delete all remote tracking entries */ |
849 | switch_thread_rwlock_wrlock(remote->rwlock); |
850 | switch_core_hash_delete_multi(remote->index, limit_hash_remote_cleanup_callback, NULL((void*)0)); |
851 | switch_thread_rwlock_unlock(remote->rwlock); |
852 | } else { |
853 | if (!zstr(remote->handle.last_sr_event->body)_zstr(remote->handle.last_sr_event->body)) { |
854 | char *data = strdup(remote->handle.last_sr_event->body); |
855 | char *p = data, *p2; |
856 | switch_time_t now = switch_epoch_time_now(NULL((void*)0)); |
857 | while (p && *p) { |
858 | /* We are getting the limit data as: |
859 | L/key/usage/rate/interval/last_checked |
860 | */ |
861 | if ((p2 = strchr(p, '\n'))) { |
862 | *p2++ = '\0'; |
863 | } |
864 | |
865 | /* Now p points at the beginning of the current line, |
866 | p2 at the start of the next one */ |
867 | if (*p == 'L') { /* Limit data */ |
868 | char *argv[5]; |
869 | int argc = switch_split(p+2, '/', argv)switch_separate_string(p+2, '/', argv, (sizeof(argv) / sizeof (argv[0]))); |
870 | |
871 | if (argc < 5) { |
872 | switch_log_printf(SWITCH_CHANNEL_LOGSWITCH_CHANNEL_ID_LOG, "mod_hash.c", (const char *)__func__, 872 , ((void*)0), SWITCH_LOG_WARNING, "[%s] Protocol error: missing argument in line: %s\n", |
873 | remote->name, p); |
874 | } else { |
875 | limit_hash_item_t *item; |
876 | switch_thread_rwlock_wrlock(remote->rwlock); |
877 | if (!(item = switch_core_hash_find(remote->index, argv[0]))) { |
878 | switch_zmalloc(item, sizeof(*item))(void)((((item = switch_calloc(1, (sizeof(*item))))) ? (void) (0) : __assert_fail ("(item = switch_calloc(1, (sizeof(*item))))" , "mod_hash.c", 878, __extension__ __PRETTY_FUNCTION__)),item ); |
879 | switch_core_hash_insert_auto_free(remote->index, argv[0], item); |
880 | } |
881 | item->total_usage = atoi(argv[1]); |
882 | item->rate_usage = atoi(argv[2]); |
883 | item->interval = atoi(argv[3]); |
884 | item->last_check = atoi(argv[4]); |
885 | item->last_update = now; |
886 | switch_thread_rwlock_unlock(remote->rwlock); |
887 | } |
888 | } |
889 | |
890 | p = p2; |
891 | } |
892 | free(data); |
893 | |
894 | /* Now free up anything that wasn't in this update since it means their usage is 0 */ |
895 | switch_thread_rwlock_wrlock(remote->rwlock); |
896 | switch_core_hash_delete_multi(remote->index, limit_hash_remote_cleanup_callback, (void*)(intptr_t)now); |
897 | switch_thread_rwlock_unlock(remote->rwlock); |
898 | } |
899 | } |
900 | } |
901 | |
902 | switch_yield(remote->interval * 1000)switch_sleep(remote->interval * 1000);; |
903 | } |
904 | |
905 | remote->thread = NULL((void*)0); |
906 | |
907 | return NULL((void*)0); |
908 | } |
909 | |
910 | static void do_config(switch_bool_t reload) |
911 | { |
912 | switch_xml_t xml = NULL((void*)0), x_lists = NULL((void*)0), x_list = NULL((void*)0), cfg = NULL((void*)0); |
913 | if ((xml = switch_xml_open_cfg("hash.conf", &cfg, NULL((void*)0)))) { |
914 | if ((x_lists = switch_xml_child(cfg, "remotes"))) { |
915 | for (x_list = switch_xml_child(x_lists, "remote"); x_list; x_list = x_list->next) { |
916 | const char *name = switch_xml_attr(x_list, "name"); |
917 | const char *host = switch_xml_attr(x_list, "host"); |
918 | const char *szport = switch_xml_attr(x_list, "port"); |
919 | const char *username = switch_xml_attr(x_list, "username"); |
920 | const char *password = switch_xml_attr(x_list, "password"); |
921 | const char *szinterval = switch_xml_attr(x_list, "interval"); |
922 | uint16_t port = 0; |
923 | int interval = 0; |
924 | limit_remote_t *remote; |
925 | switch_threadattr_t *thd_attr = NULL((void*)0); |
926 | |
927 | if (reload) { |
928 | switch_thread_rwlock_rdlock(globals.remote_hash_rwlock); |
929 | if (switch_core_hash_find(globals.remote_hash, name)) { |
930 | switch_thread_rwlock_unlock(globals.remote_hash_rwlock); |
931 | continue; |
932 | } |
933 | switch_thread_rwlock_unlock(globals.remote_hash_rwlock); |
934 | } |
935 | |
936 | if (!zstr(szport)_zstr(szport)) { |
937 | port = (uint16_t)atoi(szport); |
938 | } |
939 | |
940 | if (!zstr(szinterval)_zstr(szinterval)) { |
941 | interval = atoi(szinterval); |
942 | } |
943 | |
944 | remote = limit_remote_create(name, host, port, username, password, interval); |
945 | |
946 | remote->state = REMOTE_DOWN; |
947 | |
948 | switch_threadattr_create(&thd_attr, remote->pool); |
949 | switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE240 * 1024); |
950 | switch_thread_create(&remote->thread, thd_attr, limit_remote_thread, remote, remote->pool); |
951 | } |
952 | } |
953 | switch_xml_free(xml); |
954 | } |
955 | } |
956 | |
957 | /* INIT/DEINIT STUFF */ |
958 | SWITCH_MODULE_LOAD_FUNCTION(mod_hash_load)switch_status_t mod_hash_load (switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool) |
959 | { |
960 | switch_application_interface_t *app_interface; |
961 | switch_api_interface_t *commands_api_interface; |
962 | switch_limit_interface_t *limit_interface; |
963 | switch_status_t status; |
964 | |
965 | memset(&globals, 0, sizeof(globals)); |
966 | globals.pool = pool; |
967 | |
968 | status = switch_event_reserve_subclass(LIMIT_EVENT_USAGE)switch_event_reserve_subclass_detailed("mod_hash.c", "limit::usage" ); |
969 | if (status != SWITCH_STATUS_SUCCESS && status != SWITCH_STATUS_INUSE) { |
970 | switch_log_printf(SWITCH_CHANNEL_LOGSWITCH_CHANNEL_ID_LOG, "mod_hash.c", (const char *)__func__, 970 , ((void*)0), SWITCH_LOG_ERROR, "Couldn't register event subclass \"%s\" (%d)\n", LIMIT_EVENT_USAGE"limit::usage", status); |
971 | return SWITCH_STATUS_FALSE; |
972 | } |
973 | |
974 | switch_thread_rwlock_create(&globals.limit_hash_rwlock, globals.pool); |
975 | switch_thread_rwlock_create(&globals.db_hash_rwlock, globals.pool); |
976 | switch_thread_rwlock_create(&globals.remote_hash_rwlock, globals.pool); |
977 | switch_core_hash_init(&globals.limit_hash)switch_core_hash_init_case(&globals.limit_hash, SWITCH_TRUE ); |
978 | switch_core_hash_init(&globals.db_hash)switch_core_hash_init_case(&globals.db_hash, SWITCH_TRUE); |
979 | switch_core_hash_init(&globals.remote_hash)switch_core_hash_init_case(&globals.remote_hash, SWITCH_TRUE ); |
980 | |
981 | /* connect my internal structure to the blank pointer passed to me */ |
982 | *module_interface = switch_loadable_module_create_module_interface(pool, modname); |
983 | |
984 | /* register limit interfaces */ |
985 | SWITCH_ADD_LIMIT(limit_interface, "hash", limit_incr_hash, limit_release_hash, limit_usage_hash, limit_reset_hash, limit_status_hash, limit_interval_reset_hash)for (;;) { limit_interface = (switch_limit_interface_t *)switch_loadable_module_create_interface (*module_interface, SWITCH_LIMIT_INTERFACE); limit_interface-> incr = limit_incr_hash; limit_interface->release = limit_release_hash ; limit_interface->usage = limit_usage_hash; limit_interface ->reset = limit_reset_hash; limit_interface->interval_reset = limit_interval_reset_hash; limit_interface->status = limit_status_hash ; limit_interface->interface_name = "hash"; break; }; |
986 | |
987 | switch_scheduler_add_task(switch_epoch_time_now(NULL((void*)0)) + LIMIT_HASH_CLEANUP_INTERVAL900, limit_hash_cleanup_callback, "limit_hash_cleanup", "mod_hash", 0, NULL((void*)0), |
988 | SSHF_NONE); |
989 | |
990 | SWITCH_ADD_APP(app_interface, "hash", "Insert into the hashtable", HASH_DESC, hash_function, HASH_USAGE, SAF_SUPPORT_NOMEDIA | SAF_ZOMBIE_EXEC)for (;;) { app_interface = (switch_application_interface_t *) switch_loadable_module_create_interface(*module_interface, SWITCH_APPLICATION_INTERFACE ); app_interface->interface_name = "hash"; app_interface-> application_function = hash_function; app_interface->short_desc = "Insert into the hashtable"; app_interface->long_desc = "save data"; app_interface->syntax = "[insert|insert_ifempty|delete|delete_ifmatch]/<realm>/<key>/<val>" ; app_interface->flags = SAF_SUPPORT_NOMEDIA | SAF_ZOMBIE_EXEC ; break; } |
991 | SWITCH_ADD_API(commands_api_interface, "hash", "hash get/set", hash_api_function, "[insert|delete|select]/<realm>/<key>/<value>")for (;;) { commands_api_interface = (switch_api_interface_t * )switch_loadable_module_create_interface(*module_interface, SWITCH_API_INTERFACE ); commands_api_interface->interface_name = "hash"; commands_api_interface ->desc = "hash get/set"; commands_api_interface->function = hash_api_function; commands_api_interface->syntax = "[insert|delete|select]/<realm>/<key>/<value>" ; break; }; |
992 | SWITCH_ADD_API(commands_api_interface, "hash_dump", "dump hash/limit_hash data (used for synchronization)", hash_dump_function, HASH_DUMP_SYNTAX)for (;;) { commands_api_interface = (switch_api_interface_t * )switch_loadable_module_create_interface(*module_interface, SWITCH_API_INTERFACE ); commands_api_interface->interface_name = "hash_dump"; commands_api_interface ->desc = "dump hash/limit_hash data (used for synchronization)" ; commands_api_interface->function = hash_dump_function; commands_api_interface ->syntax = "all|limit|db [<realm>]"; break; }; |
993 | SWITCH_ADD_API(commands_api_interface, "hash_remote", "hash remote", hash_remote_function, HASH_REMOTE_SYNTAX)for (;;) { commands_api_interface = (switch_api_interface_t * )switch_loadable_module_create_interface(*module_interface, SWITCH_API_INTERFACE ); commands_api_interface->interface_name = "hash_remote"; commands_api_interface->desc = "hash remote"; commands_api_interface ->function = hash_remote_function; commands_api_interface-> syntax = "list|kill [name]|rescan"; break; }; |
994 | |
995 | switch_console_set_complete("add hash insert"); |
996 | switch_console_set_complete("add hash delete"); |
997 | switch_console_set_complete("add hash select"); |
998 | |
999 | switch_console_set_complete("add hash_remote list"); |
1000 | switch_console_set_complete("add hash_remote kill"); |
1001 | switch_console_set_complete("add hash_remote rescan"); |
1002 | |
1003 | do_config(SWITCH_FALSE); |
1004 | |
1005 | /* indicate that the module should continue to be loaded */ |
1006 | return SWITCH_STATUS_SUCCESS; |
1007 | } |
1008 | |
1009 | |
1010 | SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_hash_shutdown)switch_status_t mod_hash_shutdown (void) |
1011 | { |
1012 | switch_hash_index_t *hi = NULL((void*)0); |
1013 | switch_bool_t remote_clean = SWITCH_TRUE; |
1014 | |
1015 | switch_scheduler_del_task_group("mod_hash"); |
1016 | |
1017 | /* Kill remote connections, destroy needs a wrlock so we unlock after finding a pointer */ |
1018 | while(remote_clean) { |
1019 | void *val; |
1020 | const void *key = NULL((void*)0); |
1021 | switch_ssize_t keylen; |
1022 | limit_remote_t *item = NULL((void*)0); |
1023 | |
1024 | switch_thread_rwlock_rdlock(globals.remote_hash_rwlock); |
1025 | if ((hi = switch_core_hash_first(globals.remote_hash)switch_core_hash_first_iter(globals.remote_hash, ((void*)0)))) { |
1026 | switch_core_hash_this(hi, &key, &keylen, &val); |
1027 | item = (limit_remote_t *)val; |
1028 | } |
1029 | switch_thread_rwlock_unlock(globals.remote_hash_rwlock); |
1030 | |
1031 | if (!item) { |
1032 | remote_clean = SWITCH_FALSE; |
1033 | } else { |
1034 | limit_remote_destroy(&item); |
1035 | switch_thread_rwlock_wrlock(globals.remote_hash_rwlock); |
1036 | switch_core_hash_delete(globals.remote_hash, key); |
1037 | switch_thread_rwlock_unlock(globals.remote_hash_rwlock); |
1038 | } |
1039 | } |
1040 | |
1041 | switch_thread_rwlock_wrlock(globals.limit_hash_rwlock); |
1042 | switch_thread_rwlock_wrlock(globals.db_hash_rwlock); |
1043 | |
1044 | while ((hi = switch_core_hash_first_iter( globals.limit_hash, hi))) { |
1045 | void *val = NULL((void*)0); |
1046 | const void *key; |
1047 | switch_ssize_t keylen; |
1048 | switch_core_hash_this(hi, &key, &keylen, &val); |
1049 | free(val); |
1050 | switch_core_hash_delete(globals.limit_hash, key); |
1051 | } |
1052 | |
1053 | while ((hi = switch_core_hash_first_iter( globals.db_hash, hi))) { |
1054 | void *val = NULL((void*)0); |
1055 | const void *key; |
1056 | switch_ssize_t keylen; |
1057 | switch_core_hash_this(hi, &key, &keylen, &val); |
1058 | free(val); |
1059 | switch_core_hash_delete(globals.db_hash, key); |
1060 | } |
1061 | |
1062 | switch_core_hash_destroy(&globals.limit_hash); |
1063 | switch_core_hash_destroy(&globals.db_hash); |
1064 | switch_core_hash_destroy(&globals.remote_hash); |
1065 | |
1066 | switch_thread_rwlock_unlock(globals.limit_hash_rwlock); |
1067 | switch_thread_rwlock_unlock(globals.db_hash_rwlock); |
1068 | |
1069 | switch_thread_rwlock_destroy(globals.db_hash_rwlock); |
1070 | switch_thread_rwlock_destroy(globals.limit_hash_rwlock); |
1071 | switch_thread_rwlock_destroy(globals.remote_hash_rwlock); |
1072 | |
1073 | |
1074 | return SWITCH_STATUS_SUCCESS; |
1075 | } |
1076 | |
1077 | /* For Emacs: |
1078 | * Local Variables: |
1079 | * mode:c |
1080 | * indent-tabs-mode:t |
1081 | * tab-width:4 |
1082 | * c-basic-offset:4 |
1083 | * End: |
1084 | * For VIM: |
1085 | * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet: |
1086 | */ |