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 * Rupa Schomaker <rupa@rupa.com>
27 *
28 * mod_memcache.c -- API for memcache
29 *
30 */
31 #include <switch.h>
32 #include <switch_private.h>
33 #include <libmemcached/memcached.h>
34
35 /* Prototypes */
36 SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_memcache_shutdown);
37 SWITCH_MODULE_RUNTIME_FUNCTION(mod_memcache_runtime);
38 SWITCH_MODULE_LOAD_FUNCTION(mod_memcache_load);
39
40 /* SWITCH_MODULE_DEFINITION(name, load, shutdown, runtime)
41 * Defines a switch_loadable_module_function_table_t and a static const char[] modname
42 */
43 SWITCH_MODULE_DEFINITION(mod_memcache, mod_memcache_load, mod_memcache_shutdown, NULL);
44
45 static char *SYNTAX = "memcache <set|replace|add> <key> <value> [expiration [flags]]\n"
46 "memcache <get|getflags> <key>\n"
47 "memcache <delete> <key>\n" "memcache <increment|decrement> <key> [offset [expires [flags]]]\n" "memcache <flush>\n" "memcache <status> [verbose]\n";
48
49 static struct {
50 memcached_st *memcached;
51 char *memcached_str;
52 } globals;
53
54 #define BYTES_PER_SAMPLE 2
55
56 struct memcache_context {
57 memcached_st *memcached;
58 char *path;
59 int ok;
60 size_t offset;
61 size_t remaining;
62 void *data;
63 };
64 typedef struct memcache_context memcache_context_t;
65
66 static switch_event_node_t *NODE = NULL;
67
config_callback_memcached(switch_xml_config_item_t * data,const char * newvalue,switch_config_callback_type_t callback_type,switch_bool_t changed)68 static switch_status_t config_callback_memcached(switch_xml_config_item_t *data, const char *newvalue, switch_config_callback_type_t callback_type,
69 switch_bool_t changed)
70 {
71 switch_status_t status = SWITCH_STATUS_SUCCESS;
72 memcached_server_st *memcached_server = NULL;
73 memcached_st *newmemcached = NULL;
74 memcached_st *oldmemcached = NULL;
75 const char *memcached_str = NULL;
76 memcached_return rc;
77 unsigned int servercount;
78
79 if ((callback_type == CONFIG_LOAD || callback_type == CONFIG_RELOAD) && changed) {
80 memcached_str = newvalue;
81
82 /* initialize main ptr */
83 memcached_server = memcached_servers_parse(memcached_str);
84 if (!memcached_server) {
85 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Unable to initialize memcached data structure (server_list).\n");
86 switch_goto_status(SWITCH_STATUS_GENERR, end);
87 }
88
89 if ((servercount = memcached_server_list_count(memcached_server)) == 0) {
90 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "No memcache servers defined. Server string: %s.\n", memcached_str);
91 } else {
92 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "%u servers defined (%s).\n", servercount, memcached_str);
93 }
94
95 /* setup memcached */
96 newmemcached = memcached_create(NULL);
97 if (!newmemcached) {
98 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Unable to initialize memcached data structure (memcached_st).\n");
99 switch_goto_status(SWITCH_STATUS_GENERR, end);
100 }
101 rc = memcached_server_push(newmemcached, memcached_server);
102 if (rc != MEMCACHED_SUCCESS) {
103 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Memcache error adding server list: %s\n", memcached_strerror(newmemcached, rc));
104 switch_goto_status(SWITCH_STATUS_GENERR, end);
105 }
106 /* memcached_behavior_set(newmemcached, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL, 1); */
107
108 /* swap pointers */
109 oldmemcached = globals.memcached;
110 globals.memcached = newmemcached;
111 newmemcached = NULL;
112 }
113
114 switch_goto_status(SWITCH_STATUS_SUCCESS, end);
115
116 end:
117 if (memcached_server) {
118 memcached_server_list_free(memcached_server);
119 }
120 if (newmemcached) {
121 memcached_free(newmemcached);
122 }
123 if (oldmemcached) {
124 memcached_free(oldmemcached);
125 }
126 return status;
127 }
128
129 static switch_xml_config_string_options_t config_opt_memcache_servers = { NULL, 0, NULL }; /* anything ok */
130
131 static switch_xml_config_item_t instructions[] = {
132 /* parameter name type reloadable pointer default value options structure */
133 SWITCH_CONFIG_ITEM_CALLBACK("memcache-servers", SWITCH_CONFIG_STRING, CONFIG_REQUIRED | CONFIG_RELOAD, &globals.memcached_str, "",
134 config_callback_memcached, &config_opt_memcache_servers,
135 "host,host:port,host", "List of memcached servers."),
136 SWITCH_CONFIG_ITEM_END()
137 };
138
do_config(switch_bool_t reload)139 static switch_status_t do_config(switch_bool_t reload)
140 {
141 if (switch_xml_config_parse_module_settings("memcache.conf", reload, instructions) != SWITCH_STATUS_SUCCESS) {
142 return SWITCH_STATUS_GENERR;
143 }
144
145
146 return SWITCH_STATUS_SUCCESS;
147 }
148
event_handler(switch_event_t * event)149 static void event_handler(switch_event_t *event)
150 {
151 do_config(SWITCH_TRUE);
152 }
153
154 #if HAVE_MEMCACHED_SERVER_NAME
155 #if HAVE_MEMCACHED_INSTANCE_STP
156 #define MCD_SERVER memcached_instance_st*
157 #else
158 #define MCD_SERVER memcached_server_instance_st
159 #endif
160 #define MCD_SERVER_NAME memcached_server_name(server)
161 #define MCD_SERVER_PORT memcached_server_port(server)
162 #else
163 #define MCD_SERVER memcached_server_st*
164 #define MCD_SERVER_NAME memcached_server_name(ptr, *server)
165 #define MCD_SERVER_PORT memcached_server_port(ptr, *server)
166 #endif
167
168 struct stream_server
169 {
170 switch_stream_handle_t *stream;
171 MCD_SERVER server;
172 };
173
server_show(const memcached_st * ptr,const MCD_SERVER server,void * context)174 static memcached_return server_show(const memcached_st *ptr, const MCD_SERVER server, void *context)
175 {
176 struct stream_server *ss = (struct stream_server*) context;
177 ss->stream->write_function(ss->stream, " %s (%u)\n", MCD_SERVER_NAME, MCD_SERVER_PORT);
178 return MEMCACHED_SUCCESS;
179 }
180
server_stat(const MCD_SERVER server,const char * key,size_t key_length,const char * value,size_t value_length,void * context)181 static memcached_return server_stat(const MCD_SERVER server, const char *key, size_t key_length, const char *value, size_t value_length, void *context)
182 {
183 struct stream_server *ss = (struct stream_server*) context;
184
185 if (ss->server != server) {
186 server_show(NULL, server, context);
187 ss->server = (MCD_SERVER) server;
188 }
189
190 ss->stream->write_function(ss->stream, " %s: %s\n", key, value);
191 return MEMCACHED_SUCCESS;
192 }
193
194 #if !HAVE_MEMCACHED_STAT_EXECUTE
server_stats(memcached_st * ptr,MCD_SERVER server,void * context)195 static memcached_return server_stats(memcached_st *ptr, MCD_SERVER server, void *context)
196 {
197 char **keys, **key, *value;
198 memcached_stat_st stat;
199 memcached_return rc;
200
201 struct stream_server *ss = (struct stream_server*) context;
202
203 rc = memcached_stat_servername(&stat, NULL, MCD_SERVER_NAME, MCD_SERVER_PORT);
204 if (rc != MEMCACHED_SUCCESS) goto mcache_error;
205
206 keys = memcached_stat_get_keys(ptr, &stat, &rc);
207 if (rc != MEMCACHED_SUCCESS) goto mcache_error;
208
209 for (key = keys; *key; key++) {
210 value = memcached_stat_get_value(ptr, &stat, *key, &rc);
211
212 if (rc == MEMCACHED_SUCCESS && value) {
213 server_stat(server, *key, 0, value, 0, context);
214 switch_safe_free(value);
215 } else {
216 server_stat(server, *key, 0, "N/A", 0, context);
217 }
218 }
219
220 switch_safe_free(keys);
221 return MEMCACHED_SUCCESS;
222
223 mcache_error:
224 ss->stream->write_function(ss->stream, "-ERR %s\n", memcached_strerror(ptr, rc));
225 return MEMCACHED_SUCCESS;
226 }
227 #endif
228
SWITCH_STANDARD_API(memcache_function)229 SWITCH_STANDARD_API(memcache_function)
230 {
231 switch_status_t status;
232 char *argv[5] = { 0 };
233 int argc;
234 char *subcmd = NULL;
235 char *key = NULL;
236 char *val = NULL;
237 char *expires_str = NULL;
238 char *flags_str = NULL;
239 char *mydata = NULL;
240 size_t string_length = 0;
241 time_t expires = 0;
242 uint32_t flags = 0;
243 unsigned int server_count = 0;
244
245 memcached_return rc = 0;
246 memcached_st *memcached = NULL;
247 memcached_stat_st *stat = NULL;
248
249 if (zstr(cmd)) {
250 goto usage;
251 }
252
253 mydata = strdup(cmd);
254 if ((argc = switch_separate_string(mydata, ' ', argv, (sizeof(argv) / sizeof(argv[0]))))) {
255 if (argc < 1) {
256 goto usage;
257 }
258
259 /* clone memcached struct so we're thread safe */
260 memcached = memcached_clone(NULL, globals.memcached);
261 if (!memcached) {
262 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Error cloning memcached object");
263 stream->write_function(stream, "-ERR Error cloning memcached object\n");
264 }
265
266 subcmd = argv[0];
267
268 if ((!strcasecmp(subcmd, "set") || !strcasecmp(subcmd, "replace") || !strcasecmp(subcmd, "add")) && argc > 2) {
269 key = argv[1];
270 val = argv[2];
271
272 if (argc > 3) {
273 expires_str = argv[3];
274 expires = (time_t) strtol(expires_str, NULL, 10);
275 }
276 if (argc > 4) {
277 flags_str = argv[4];
278 flags = (uint32_t) strtol(flags_str, NULL, 16);
279 }
280 if (!strcasecmp(subcmd, "set")) {
281 rc = memcached_set(memcached, key, strlen(key), val, strlen(val), expires, flags);
282 } else if (!strcasecmp(subcmd, "replace")) {
283 rc = memcached_replace(memcached, key, strlen(key), val, strlen(val), expires, flags);
284 } else if (!strcasecmp(subcmd, "add")) {
285 rc = memcached_add(memcached, key, strlen(key), val, strlen(val), expires, flags);
286 }
287
288 if (rc == MEMCACHED_SUCCESS) {
289 stream->write_function(stream, "+OK\n");
290 } else {
291 switch_goto_status(SWITCH_STATUS_SUCCESS, mcache_error);
292 }
293 } else if (!strcasecmp(subcmd, "get") && argc > 1) {
294 key = argv[1];
295
296 val = memcached_get(memcached, key, strlen(key), &string_length, &flags, &rc);
297 if (rc == MEMCACHED_SUCCESS) {
298 stream->raw_write_function(stream, (uint8_t*)val, (int)string_length);
299 } else {
300 switch_safe_free(val);
301 switch_goto_status(SWITCH_STATUS_SUCCESS, mcache_error);
302 }
303 switch_safe_free(val);
304 } else if (!strcasecmp(subcmd, "getflags") && argc > 1) {
305 key = argv[1];
306
307 val = memcached_get(memcached, key, strlen(key), &string_length, &flags, &rc);
308 if (rc == MEMCACHED_SUCCESS) {
309 stream->write_function(stream, "%x", flags);
310 } else {
311 switch_safe_free(val);
312 switch_goto_status(SWITCH_STATUS_SUCCESS, mcache_error);
313 }
314 switch_safe_free(val);
315 } else if ((!strcasecmp(subcmd, "increment") || !strcasecmp(subcmd, "decrement")) && argc > 1) {
316 uint64_t ivalue = 0;
317 unsigned int offset = 1;
318 switch_bool_t increment = SWITCH_TRUE;
319 char *svalue = NULL;
320 key = argv[1];
321
322 if (argc > 2) {
323 offset = (unsigned int) strtol(argv[2], NULL, 10);
324 svalue = argv[2];
325 } else {
326 svalue = "1";
327 }
328 if (argc > 3) {
329 expires_str = argv[3];
330 expires = (time_t) strtol(expires_str, NULL, 10);
331 }
332 if (argc > 4) {
333 flags_str = argv[4];
334 flags = (uint32_t) strtol(flags_str, NULL, 16);
335 }
336
337 if (!strcasecmp(subcmd, "increment")) {
338 increment = SWITCH_TRUE;
339 rc = memcached_increment(memcached, key, strlen(key), offset, &ivalue);
340 } else if (!strcasecmp(subcmd, "decrement")) {
341 increment = SWITCH_FALSE;
342 rc = memcached_decrement(memcached, key, strlen(key), offset, &ivalue);
343 }
344 if (rc == MEMCACHED_NOTFOUND) {
345 /* ok, trying to incr / decr a value that doesn't exist yet.
346 Try to add an appropriate initial value. If someone else beat
347 us to it, then redo incr/decr. Otherwise we're good.
348 */
349 rc = memcached_add(memcached, key, strlen(key), (increment) ? svalue : "0", strlen(svalue), expires, flags);
350 if (rc == MEMCACHED_SUCCESS) {
351 ivalue = (increment) ? offset : 0;
352 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Initialized inc/dec memcache key: %s to value %d\n", key,
353 offset);
354 } else {
355 if (increment) {
356 rc = memcached_increment(memcached, key, strlen(key), offset, &ivalue);
357 } else {
358 rc = memcached_decrement(memcached, key, strlen(key), offset, &ivalue);
359 }
360 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG,
361 "Someone else created incr/dec memcache key, resubmitting inc/dec request.\n");
362 }
363 }
364 if (rc == MEMCACHED_SUCCESS) {
365 stream->write_function(stream, "%ld", ivalue);
366 } else {
367 switch_goto_status(SWITCH_STATUS_SUCCESS, mcache_error);
368 }
369 } else if (!strcasecmp(subcmd, "delete") && argc > 1) {
370 key = argv[1];
371 if (argc > 2) {
372 expires_str = argv[2];
373 expires = (time_t) strtol(expires_str, NULL, 10);
374 }
375 rc = memcached_delete(memcached, key, strlen(key), expires);
376 if (rc == MEMCACHED_SUCCESS) {
377 stream->write_function(stream, "+OK\n", key);
378 } else {
379 switch_goto_status(SWITCH_STATUS_SUCCESS, mcache_error);
380 }
381 } else if (!strcasecmp(subcmd, "flush")) {
382 if (argc > 1) {
383 expires_str = argv[1];
384 expires = (time_t) strtol(expires_str, NULL, 10);
385 }
386 rc = memcached_flush(memcached, expires);
387 if (rc == MEMCACHED_SUCCESS) {
388 stream->write_function(stream, "+OK\n", key);
389 } else {
390 switch_goto_status(SWITCH_STATUS_SUCCESS, mcache_error);
391 }
392 } else if (!strcasecmp(subcmd, "status")) {
393 struct stream_server ss;
394 ss.stream = stream;
395 ss.server = NULL;
396
397 stream->write_function(stream, "Lib version: %s\n", memcached_lib_version());
398
399 server_count = memcached_server_count(memcached);
400 stream->write_function(stream, "Servers: %d\n", server_count);
401
402 if (argc > 1 && !strcasecmp(argv[1], "verbose")) {
403 #if HAVE_MEMCACHED_STAT_EXECUTE
404 rc = memcached_stat_execute(memcached, NULL, server_stat, (void*) &ss);
405 #else
406 memcached_server_function callbacks[] = { (memcached_server_function) server_stats };
407 rc = memcached_server_cursor(memcached, callbacks, (void*) &ss, 1);
408 #endif
409 } else {
410 memcached_server_function callbacks[] = { (memcached_server_function) server_show };
411 rc = memcached_server_cursor(memcached, callbacks, (void*) &ss, 1);
412 }
413
414 if (rc != MEMCACHED_SUCCESS) {
415 switch_goto_status(SWITCH_STATUS_SUCCESS, mcache_error);
416 }
417 } else {
418 goto usage;
419 }
420 }
421 switch_goto_status(SWITCH_STATUS_SUCCESS, done);
422
423 mcache_error:
424 if (rc != MEMCACHED_NOTFOUND) {
425 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Error while running command %s: %s\n", subcmd,
426 memcached_strerror(memcached, rc));
427 }
428 stream->write_function(stream, "-ERR %s\n", memcached_strerror(memcached, rc));
429 goto done;
430
431 usage:
432 stream->write_function(stream, "-ERR\n%s\n", SYNTAX);
433 switch_goto_status(SWITCH_STATUS_SUCCESS, done);
434
435 done:
436 if (memcached) {
437 memcached_quit(memcached);
438 memcached_free(memcached);
439 }
440 switch_safe_free(mydata);
441 switch_safe_free(stat);
442
443 return status;
444 }
445
446
memcache_file_open(switch_file_handle_t * handle,const char * path)447 static switch_status_t memcache_file_open(switch_file_handle_t *handle, const char *path)
448 {
449 memcache_context_t *context;
450 size_t string_length = 0;
451 uint32_t flags = 0;
452 memcached_return rc;
453
454 if (handle->offset_pos) {
455 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Offset unsupported.\n");
456 return SWITCH_STATUS_GENERR;
457 }
458
459 context = switch_core_alloc(handle->memory_pool, sizeof(*context));
460
461 /* Clone memcached struct so we're thread safe */
462 context->memcached = memcached_clone(NULL, globals.memcached);
463 if (!context->memcached) {
464 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error cloning memcached object\n");
465 return SWITCH_STATUS_FALSE;
466 }
467
468 /* All of the actual data is read into the buffer here, the memcache_file_read
469 * function just iterates over the buffer
470 */
471 if (switch_test_flag(handle, SWITCH_FILE_FLAG_READ)) {
472 handle->private_info = context;
473
474 context->data = memcached_get(context->memcached, path, strlen(path), &string_length, &flags, &rc);
475
476 if (context->data && rc == MEMCACHED_SUCCESS) {
477 context->ok = 1;
478 context->offset = 0;
479 context->remaining = string_length / BYTES_PER_SAMPLE;
480 return SWITCH_STATUS_SUCCESS;
481 } else {
482 memcached_free(context->memcached);
483 context->memcached = NULL;
484 switch_safe_free(context->data);
485 context->data = NULL;
486 context->ok = 0;
487 return SWITCH_STATUS_FALSE;
488 }
489 } else if (switch_test_flag(handle, SWITCH_FILE_FLAG_WRITE)) {
490 if (switch_test_flag(handle, SWITCH_FILE_WRITE_OVER)) {
491 memcached_free(context->memcached);
492 context->memcached = NULL;
493 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unsupported file mode.\n");
494 return SWITCH_STATUS_GENERR;
495 }
496
497 context->path = switch_core_strdup(handle->memory_pool, path);
498
499 if (! switch_test_flag(handle, SWITCH_FILE_WRITE_APPEND)) {
500 /* If not appending, we need to write an empty string to the key so that
501 * memcache_file_write appends do the right thing
502 */
503 rc = memcached_set(context->memcached, context->path, strlen(context->path), "", 0, 0, 0);
504 if (rc != MEMCACHED_SUCCESS) {
505 memcached_free(context->memcached);
506 context->memcached = NULL;
507 return SWITCH_STATUS_GENERR;
508 }
509 }
510
511 context->ok = 1;
512 handle->private_info = context;
513
514 return SWITCH_STATUS_SUCCESS;
515 }
516
517 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "File opened with unknown flags!\n");
518 return SWITCH_STATUS_GENERR;
519 }
520
memcache_file_close(switch_file_handle_t * handle)521 static switch_status_t memcache_file_close(switch_file_handle_t *handle)
522 {
523 memcache_context_t *memcache_context = handle->private_info;
524
525 if (memcache_context->data) {
526 switch_safe_free(memcache_context->data);
527 memcache_context->data = NULL;
528 }
529 if (memcache_context->memcached) {
530 memcached_free(memcache_context->memcached);
531 memcache_context->memcached = NULL;
532 }
533
534 return SWITCH_STATUS_SUCCESS;
535 }
536
memcache_file_read(switch_file_handle_t * handle,void * data,size_t * len)537 static switch_status_t memcache_file_read(switch_file_handle_t *handle, void *data, size_t *len)
538 {
539 memcache_context_t *context= handle->private_info;
540
541 if (context->ok) {
542 if (context->remaining <= 0) {
543 return SWITCH_STATUS_FALSE;
544 }
545
546 if (*len > (size_t) context->remaining) {
547 *len = context->remaining;
548 }
549
550 memcpy(data, (uint8_t *)context->data + (context->offset * BYTES_PER_SAMPLE), *len * BYTES_PER_SAMPLE);
551
552 context->offset += (int32_t)*len;
553 context->remaining -= (int32_t)*len;
554
555 return SWITCH_STATUS_SUCCESS;
556 }
557
558 return SWITCH_STATUS_FALSE;
559 }
560
memcache_file_write(switch_file_handle_t * handle,void * data,size_t * len)561 static switch_status_t memcache_file_write(switch_file_handle_t *handle, void *data, size_t *len)
562 {
563 memcache_context_t *context = handle->private_info;
564 memcached_return rc;
565
566 /* Append this chunk */
567 if (context->ok) {
568 rc = memcached_append(context->memcached, context->path, strlen(context->path), data, *len, 0, 0);
569
570 if (rc != MEMCACHED_SUCCESS) {
571 context->ok = 0;
572 return SWITCH_STATUS_FALSE;
573 }
574
575 return SWITCH_STATUS_SUCCESS;
576 }
577
578 return SWITCH_STATUS_FALSE;
579 }
580
581
582 static char *supported_formats[SWITCH_MAX_CODECS] = { 0 };
583
584 /* Macro expands to: switch_status_t mod_memcache_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool) */
SWITCH_MODULE_LOAD_FUNCTION(mod_memcache_load)585 SWITCH_MODULE_LOAD_FUNCTION(mod_memcache_load)
586 {
587 switch_api_interface_t *api_interface;
588 switch_file_interface_t *file_interface;
589 /* connect my internal structure to the blank pointer passed to me */
590 *module_interface = switch_loadable_module_create_module_interface(pool, modname);
591
592 memset(&globals, 0, sizeof(globals));
593
594 do_config(SWITCH_FALSE);
595
596 if ((switch_event_bind_removable(modname, SWITCH_EVENT_RELOADXML, NULL, event_handler, NULL, &NODE) != SWITCH_STATUS_SUCCESS)) {
597 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't bind event!\n");
598 return SWITCH_STATUS_TERM;
599 }
600
601 SWITCH_ADD_API(api_interface, "memcache", "Memcache API", memcache_function, "syntax");
602
603 /* file interface */
604 supported_formats[0] = "memcache";
605 file_interface = switch_loadable_module_create_interface(*module_interface, SWITCH_FILE_INTERFACE);
606 file_interface->interface_name = modname;
607 file_interface->extens = supported_formats;
608 file_interface->file_open = memcache_file_open;
609 file_interface->file_close = memcache_file_close;
610 file_interface->file_read = memcache_file_read;
611 file_interface->file_write = memcache_file_write;
612
613 /* indicate that the module should continue to be loaded */
614 return SWITCH_STATUS_NOUNLOAD;
615 }
616
617 /*
618 Called when the system shuts down
619 Macro expands to: switch_status_t mod_memcache_shutdown() */
SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_memcache_shutdown)620 SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_memcache_shutdown)
621 {
622 /* Cleanup dynamically allocated config settings */
623 switch_xml_config_cleanup(instructions);
624 if (globals.memcached) {
625 memcached_free(globals.memcached);
626 }
627
628 switch_event_unbind(&NODE);
629
630 return SWITCH_STATUS_SUCCESS;
631 }
632
633
634 /*
635 If it exists, this is called in it's own thread when the module-load completes
636 If it returns anything but SWITCH_STATUS_TERM it will be called again automatically
637 Macro expands to: switch_status_t mod_memcache_runtime()
638 SWITCH_MODULE_RUNTIME_FUNCTION(mod_memcache_runtime)
639 {
640 while(looping)
641 {
642 switch_cond_next();
643 }
644 return SWITCH_STATUS_TERM;
645 }
646 */
647
648 /* For Emacs:
649 * Local Variables:
650 * mode:c
651 * indent-tabs-mode:t
652 * tab-width:4
653 * c-basic-offset:4
654 * End:
655 * For VIM:
656 * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet
657 */
658