Skip to content
Snippets Groups Projects
cdr.c 33.1 KiB
Newer Older
  • Learn to ignore specific revisions
  • 	curr = batch->size++;
    	ast_mutex_unlock(&cdr_batch_lock);
    
    	/* if we have enough stuff to post, then do it */
    	if (curr >= (batchsize - 1))
    		submit_unscheduled_batch();
    }
    
    static void *do_cdr(void *data)
    {
    	struct timeval now;
    	struct timespec timeout;
    	int schedms;
    	int numevents = 0;
    
    	for(;;) {
    		gettimeofday(&now, NULL);
    		schedms = ast_sched_wait(sched);
    		/* this shouldn't happen, but provide a 1 second default just in case */
    		if (schedms <= 0)
    			schedms = 1000;
    		timeout.tv_sec = now.tv_sec + (schedms / 1000);
    		timeout.tv_nsec = (now.tv_usec * 1000) + ((schedms % 1000) * 1000);
    		/* prevent stuff from clobbering cdr_pending_cond, then wait on signals sent to it until the timeout expires */
    
    		ast_mutex_lock(&cdr_pending_lock);
    
    		pthread_cond_timedwait(&cdr_pending_cond, &cdr_pending_lock, &timeout);
    		numevents = ast_sched_runq(sched);
    
    		ast_mutex_unlock(&cdr_pending_lock);
    
    		if (option_debug > 1)
    			ast_log(LOG_DEBUG, "Processed %d scheduled CDR batches from the run queue\n", numevents);
    	}
    
    	return NULL;
    }
    
    static int handle_cli_status(int fd, int argc, char *argv[])
    {
    	struct ast_cdr_beitem *beitem=NULL;
    	int cnt=0;
    	long nextbatchtime=0;
    
    	if (argc > 2)
    		return RESULT_SHOWUSAGE;
    
    	ast_cli(fd, "CDR logging: %s\n", enabled ? "enabled" : "disabled");
    	ast_cli(fd, "CDR mode: %s\n", batchmode ? "batch" : "simple");
    	if (enabled) {
    		if (batchmode) {
    			if (batch)
    				cnt = batch->size;
    			if (cdr_sched > -1)
    				nextbatchtime = ast_sched_when(sched, cdr_sched);
    			ast_cli(fd, "CDR safe shut down: %s\n", batchsafeshutdown ? "enabled" : "disabled");
    			ast_cli(fd, "CDR batch threading model: %s\n", batchscheduleronly ? "scheduler only" : "scheduler plus separate threads");
    			ast_cli(fd, "CDR current batch size: %d record(s)\n", cnt);
    			ast_cli(fd, "CDR maximum batch size: %d record(s)\n", batchsize);
    			ast_cli(fd, "CDR maximum batch time: %d second(s)\n", batchtime);
    			ast_cli(fd, "CDR next scheduled batch processing time: %ld second(s)\n", nextbatchtime);
    		}
    		AST_LIST_LOCK(&be_list);
    		AST_LIST_TRAVERSE(&be_list, beitem, list) {
    			ast_cli(fd, "CDR registered backend: %s\n", beitem->name);
    		}
    		AST_LIST_UNLOCK(&be_list);
    	}
    
    	return 0;
    }
    
    static int handle_cli_submit(int fd, int argc, char *argv[])
    {
    	if (argc > 2)
    		return RESULT_SHOWUSAGE;
    
    	submit_unscheduled_batch();
    	ast_cli(fd, "Submitted CDRs to backend engines for processing.  This may take a while.\n");
    
    	return 0;
    }
    
    static struct ast_cli_entry cli_submit = {
    	.cmda = { "cdr", "submit", NULL },
    	.handler = handle_cli_submit,
    	.summary = "Posts all pending batched CDR data",
    	.usage =
    	"Usage: cdr submit\n"
    	"       Posts all pending batched CDR data to the configured CDR backend engine modules.\n"
    };
    
    static struct ast_cli_entry cli_status = {
    	.cmda = { "cdr", "status", NULL },
    	.handler = handle_cli_status,
    	.summary = "Display the CDR status",
    	.usage =
    	"Usage: cdr status\n"
    	"	Displays the Call Detail Record engine system status.\n"
    };
    
    static int do_reload(void)
    {
    	struct ast_config *config;
    	const char *enabled_value;
    	const char *batched_value;
    	const char *scheduleronly_value;
    	const char *batchsafeshutdown_value;
    	const char *size_value;
    	const char *time_value;
    	int cfg_size;
    	int cfg_time;
    	int was_enabled;
    	int was_batchmode;
    	int res=0;
    	pthread_attr_t attr;
    
    	ast_mutex_lock(&cdr_batch_lock);
    
    	batchsize = BATCH_SIZE_DEFAULT;
    	batchtime = BATCH_TIME_DEFAULT;
    	batchscheduleronly = BATCH_SCHEDULER_ONLY_DEFAULT;
    	batchsafeshutdown = BATCH_SAFE_SHUTDOWN_DEFAULT;
    	was_enabled = enabled;
    	was_batchmode = batchmode;
    	enabled = 1;
    	batchmode = 0;
    
    	/* don't run the next scheduled CDR posting while reloading */
    	if (cdr_sched > -1)
    		ast_sched_del(sched, cdr_sched);
    
    	if ((config = ast_config_load("cdr.conf"))) {
    		if ((enabled_value = ast_variable_retrieve(config, "general", "enable"))) {
    			enabled = ast_true(enabled_value);
    		}
    		if ((batched_value = ast_variable_retrieve(config, "general", "batch"))) {
    			batchmode = ast_true(batched_value);
    		}
    		if ((scheduleronly_value = ast_variable_retrieve(config, "general", "scheduleronly"))) {
    			batchscheduleronly = ast_true(scheduleronly_value);
    		}
    		if ((batchsafeshutdown_value = ast_variable_retrieve(config, "general", "safeshutdown"))) {
    			batchsafeshutdown = ast_true(batchsafeshutdown_value);
    		}
    		if ((size_value = ast_variable_retrieve(config, "general", "size"))) {
    			if (sscanf(size_value, "%d", &cfg_size) < 1)
    				ast_log(LOG_WARNING, "Unable to convert '%s' to a numeric value.\n", size_value);
    			else if (size_value < 0)
    				ast_log(LOG_WARNING, "Invalid maximum batch size '%d' specified, using default\n", cfg_size);
    			else
    				batchsize = cfg_size;
    		}
    		if ((time_value = ast_variable_retrieve(config, "general", "time"))) {
    			if (sscanf(time_value, "%d", &cfg_time) < 1)
    				ast_log(LOG_WARNING, "Unable to convert '%s' to a numeric value.\n", time_value);
    			else if (time_value < 0)
    				ast_log(LOG_WARNING, "Invalid maximum batch time '%d' specified, using default\n", cfg_time);
    			else
    				batchtime = cfg_time;
    		}
    	}
    
    	if (enabled && !batchmode) {
    		ast_log(LOG_NOTICE, "CDR simple logging enabled.\n");
    	} else if (enabled && batchmode) {
    		cdr_sched = ast_sched_add(sched, batchtime * 1000, submit_scheduled_batch, NULL);
    		ast_log(LOG_NOTICE, "CDR batch mode logging enabled, first of either size %d or time %d seconds.\n", batchsize, batchtime);
    	} else {
    		ast_log(LOG_NOTICE, "CDR logging disabled, data will be lost.\n");
    	}
    
    	/* if this reload enabled the CDR batch mode, create the background thread
    	   if it does not exist */
    	if (enabled && batchmode && (!was_enabled || !was_batchmode) && (cdr_thread == AST_PTHREADT_NULL)) {
    		pthread_cond_init(&cdr_pending_cond, NULL);
    		pthread_attr_init(&attr);
    		pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
    		if (ast_pthread_create(&cdr_thread, &attr, do_cdr, NULL) < 0) {
    			ast_log(LOG_ERROR, "Unable to start CDR thread.\n");
    			ast_sched_del(sched, cdr_sched);
    		} else {
    			ast_cli_register(&cli_submit);
    			ast_register_atexit(ast_cdr_engine_term);
    			res = 0;
    		}
    	/* if this reload disabled the CDR and/or batch mode and there is a background thread,
    	   kill it */
    	} else if (((!enabled && was_enabled) || (!batchmode && was_batchmode)) && (cdr_thread != AST_PTHREADT_NULL)) {
    		/* wake up the thread so it will exit */
    		pthread_cancel(cdr_thread);
    		pthread_kill(cdr_thread, SIGURG);
    		pthread_join(cdr_thread, NULL);
    		cdr_thread = AST_PTHREADT_NULL;
    		pthread_cond_destroy(&cdr_pending_cond);
    		ast_cli_unregister(&cli_submit);
    		ast_unregister_atexit(ast_cdr_engine_term);
    		res = 0;
    		/* if leaving batch mode, then post the CDRs in the batch,
    		   and don't reschedule, since we are stopping CDR logging */
    		if (!batchmode && was_batchmode) {
    			ast_cdr_engine_term();
    		}
    	} else {
    		res = 0;
    	}
    
    	ast_mutex_unlock(&cdr_batch_lock);
    	ast_config_destroy(config);
    
    	return res;
    }
    
    int ast_cdr_engine_init(void)
    {
    	int res;
    
    	sched = sched_context_create();
    	if (!sched) {
    		ast_log(LOG_ERROR, "Unable to create schedule context.\n");
    		return -1;
    	}
    
    	ast_cli_register(&cli_status);
    
    	res = do_reload();
    	if (res) {
    		ast_mutex_lock(&cdr_batch_lock);
    		res = init_batch();
    		ast_mutex_unlock(&cdr_batch_lock);
    	}
    
    	return res;
    }
    
    /* This actually gets called a couple of times at shutdown.  Once, before we start
       hanging up channels, and then again, after the channel hangup timeout expires */
    void ast_cdr_engine_term(void)
    {
    	ast_cdr_submit_batch(batchsafeshutdown);
    }
    
    void ast_cdr_engine_reload(void)
    {
    	do_reload();
    }