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