Skip to content
Snippets Groups Projects
cdr.c 33 KiB
Newer Older
Mark Spencer's avatar
Mark Spencer committed
/*
 * Asterisk -- A telephony toolkit for Linux.
 *
 * Call Detail Record API 
 * 
 * Copyright (C) 1999 - 2005, Digium, Inc.
Mark Spencer's avatar
Mark Spencer committed
 *
 * Mark Spencer <markster@digium.com>
Mark Spencer's avatar
Mark Spencer committed
 *
 * This program is free software, distributed under the terms of
 * the GNU General Public License.
 *
 * Includes code and algorithms from the Zapata library.
 *
 */

#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include "asterisk.h"

ASTERISK_FILE_VERSION("$Revision$")

#include "asterisk/lock.h"
#include "asterisk/channel.h"
#include "asterisk/cdr.h"
#include "asterisk/logger.h"
#include "asterisk/callerid.h"
#include "asterisk/causes.h"
#include "asterisk/options.h"
Kevin P. Fleming's avatar
Kevin P. Fleming committed
#include "asterisk/linkedlists.h"
#include "asterisk/sched.h"
#include "asterisk/config.h"
#include "asterisk/cli.h"
#include "asterisk/module.h"
Mark Spencer's avatar
Mark Spencer committed

int ast_default_amaflags = AST_CDR_DOCUMENTATION;
char ast_default_accountcode[AST_MAX_ACCOUNT_CODE] = "";
Kevin P. Fleming's avatar
Kevin P. Fleming committed
struct ast_cdr_beitem {
Mark Spencer's avatar
Mark Spencer committed
	char name[20];
	char desc[80];
	ast_cdrbe be;
Kevin P. Fleming's avatar
Kevin P. Fleming committed
	AST_LIST_ENTRY(ast_cdr_beitem) list;
};
Kevin P. Fleming's avatar
Kevin P. Fleming committed
static AST_LIST_HEAD_STATIC(be_list, ast_cdr_beitem);
struct ast_cdr_batch_item {
	struct ast_cdr *cdr;
	struct ast_cdr_batch_item *next;
};

static struct ast_cdr_batch {
	int size;
	struct ast_cdr_batch_item *head;
	struct ast_cdr_batch_item *tail;
} *batch = NULL;

static struct sched_context *sched;
static int cdr_sched = -1;
static pthread_t cdr_thread = AST_PTHREADT_NULL;

#define BATCH_SIZE_DEFAULT 100
#define BATCH_TIME_DEFAULT 300
#define BATCH_SCHEDULER_ONLY_DEFAULT 0
#define BATCH_SAFE_SHUTDOWN_DEFAULT 1

static int enabled;
static int batchmode;
static int batchsize;
static int batchtime;
static int batchscheduleronly;
static int batchsafeshutdown;

AST_MUTEX_DEFINE_STATIC(cdr_batch_lock);

/* these are used to wake up the CDR thread when there's work to do */
AST_MUTEX_DEFINE_STATIC(cdr_pending_lock);
pthread_cond_t cdr_pending_cond;

Mark Spencer's avatar
Mark Spencer committed
/*
 * We do a lot of checking here in the CDR code to try to be sure we don't ever let a CDR slip
 * through our fingers somehow.  If someone allocates a CDR, it must be completely handled normally
 * or a WARNING shall be logged, so that we can best keep track of any escape condition where the CDR
 * isn't properly generated and posted.
 */

int ast_cdr_register(char *name, char *desc, ast_cdrbe be)
{
	struct ast_cdr_beitem *i;
Kevin P. Fleming's avatar
Kevin P. Fleming committed

Mark Spencer's avatar
Mark Spencer committed
	if (!name)
		return -1;
	if (!be) {
		ast_log(LOG_WARNING, "CDR engine '%s' lacks backend\n", name);
		return -1;
	}
Kevin P. Fleming's avatar
Kevin P. Fleming committed

	AST_LIST_LOCK(&be_list);
	AST_LIST_TRAVERSE(&be_list, i, list) {
Mark Spencer's avatar
Mark Spencer committed
		if (!strcasecmp(name, i->name))
			break;
	}
Kevin P. Fleming's avatar
Kevin P. Fleming committed
	AST_LIST_UNLOCK(&be_list);

Mark Spencer's avatar
Mark Spencer committed
	if (i) {
		ast_log(LOG_WARNING, "Already have a CDR backend called '%s'\n", name);
		return -1;
	}
Kevin P. Fleming's avatar
Kevin P. Fleming committed

	i = malloc(sizeof(*i));
Mark Spencer's avatar
Mark Spencer committed
	if (!i) 	
		return -1;
Kevin P. Fleming's avatar
Kevin P. Fleming committed

	memset(i, 0, sizeof(*i));
Mark Spencer's avatar
Mark Spencer committed
	i->be = be;
Kevin P. Fleming's avatar
Kevin P. Fleming committed
	ast_copy_string(i->name, name, sizeof(i->name));
	ast_copy_string(i->desc, desc, sizeof(i->desc));

	AST_LIST_LOCK(&be_list);
	AST_LIST_INSERT_HEAD(&be_list, i, list);
	AST_LIST_UNLOCK(&be_list);

Mark Spencer's avatar
Mark Spencer committed
	return 0;
}

void ast_cdr_unregister(char *name)
{
Kevin P. Fleming's avatar
Kevin P. Fleming committed
	struct ast_cdr_beitem *i = NULL;

	AST_LIST_LOCK(&be_list);
	AST_LIST_TRAVERSE_SAFE_BEGIN(&be_list, i, list) {
Mark Spencer's avatar
Mark Spencer committed
		if (!strcasecmp(name, i->name)) {
Kevin P. Fleming's avatar
Kevin P. Fleming committed
			AST_LIST_REMOVE_CURRENT(&be_list, list);
			if (option_verbose > 1)
				ast_verbose(VERBOSE_PREFIX_2 "Unregistered '%s' CDR backend\n", name);
			free(i);
Mark Spencer's avatar
Mark Spencer committed
			break;
		}
	}
Kevin P. Fleming's avatar
Kevin P. Fleming committed
	AST_LIST_TRAVERSE_SAFE_END;
	AST_LIST_UNLOCK(&be_list);
static const char *ast_cdr_getvar_internal(struct ast_cdr *cdr, const char *name, int recur) 
{
	struct ast_var_t *variables;
	struct varshead *headp;

Kevin P. Fleming's avatar
Kevin P. Fleming committed
	if (!name || ast_strlen_zero(name))
		return NULL;

	while (cdr) {
		headp = &cdr->varshead;
Kevin P. Fleming's avatar
Kevin P. Fleming committed
		AST_LIST_TRAVERSE(headp, variables, entries) {
			if (!strcasecmp(name, ast_var_name(variables)))
				return ast_var_value(variables);
Kevin P. Fleming's avatar
Kevin P. Fleming committed
		if (!recur)
			break;
		cdr = cdr->next;
	}
Kevin P. Fleming's avatar
Kevin P. Fleming committed

	return NULL;
}

void ast_cdr_getvar(struct ast_cdr *cdr, const char *name, char **ret, char *workspace, int workspacelen, int recur) 
{
	struct tm tm;
	time_t t;
	const char *fmt = "%Y-%m-%d %T";
Kevin P. Fleming's avatar
Kevin P. Fleming committed
	const char *varbuf;

	*ret = NULL;
	/* special vars (the ones from the struct ast_cdr when requested by name) 
	   I'd almost say we should convert all the stringed vals to vars */

Kevin P. Fleming's avatar
Kevin P. Fleming committed
	if (!strcasecmp(name, "clid"))
		ast_copy_string(workspace, cdr->clid, workspacelen);
	else if (!strcasecmp(name, "src"))
		ast_copy_string(workspace, cdr->src, workspacelen);
	else if (!strcasecmp(name, "dst"))
		ast_copy_string(workspace, cdr->dst, workspacelen);
	else if (!strcasecmp(name, "dcontext"))
		ast_copy_string(workspace, cdr->dcontext, workspacelen);
	else if (!strcasecmp(name, "channel"))
		ast_copy_string(workspace, cdr->channel, workspacelen);
	else if (!strcasecmp(name, "dstchannel"))
		ast_copy_string(workspace, cdr->dstchannel, workspacelen);
	else if (!strcasecmp(name, "lastapp"))
		ast_copy_string(workspace, cdr->lastapp, workspacelen);
	else if (!strcasecmp(name, "lastdata"))
		ast_copy_string(workspace, cdr->lastdata, workspacelen);
	else if (!strcasecmp(name, "start")) {
		t = cdr->start.tv_sec;
Loading
Loading full blame...