OpenSolaris_b135/lib/libsip/common/sip_logging.c

Compare this file to the similar file:
Show the results in this format:

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <stdlib.h>
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <sip.h>

#include "sip_msg.h"
#include "sip_miscdefs.h"
#include "sip_xaction.h"
#include "sip_dialog.h"

#define	TIME_BUF_SIZE	50

/*
 * Contains API's which enable/disable transaction or dialog logging,
 * API's which records/measures SIP Traffic.
 */
/*
 * Needed for measuring SIP traffic counters.
 */
sip_traffic_counters_t	sip_counters;

/*
 * Needed for dialog/transaction logging.
 */
sip_logfile_t trans_log;
sip_logfile_t dialog_log;

/*
 * This function increments the appropriate inbound/outbound counters for
 * SIP requests/responses.
 */
void
sip_measure_traffic(boolean_t is_request, sip_method_t method, int resp_code,
    boolean_t outbound, int msg_size)
{
#ifdef	__solaris__
	assert(mutex_held(&sip_counters.sip_counter_mutex));
#endif
	if (outbound)
		sip_counters.sip_total_bytes_sent += msg_size;
	else
		sip_counters.sip_total_bytes_rcvd += msg_size;

	if (is_request) {
		if (outbound)
			++sip_counters.sip_total_req_sent;
		else
			++sip_counters.sip_total_req_rcvd;
		switch (method) {
			case INVITE:
				if (outbound)
					++sip_counters.sip_invite_req_sent;
				else
					++sip_counters.sip_invite_req_rcvd;
				break;
			case ACK:
				if (outbound)
					++sip_counters.sip_ack_req_sent;
				else
					++sip_counters.sip_ack_req_rcvd;
				break;
			case OPTIONS:
				if (outbound)
					++sip_counters.sip_options_req_sent;
				else
					++sip_counters.sip_options_req_rcvd;
				break;
			case BYE:
				if (outbound)
					++sip_counters.sip_bye_req_sent;
				else
					++sip_counters.sip_bye_req_rcvd;
				break;
			case CANCEL:
				if (outbound)
					++sip_counters.sip_cancel_req_sent;
				else
					++sip_counters.sip_cancel_req_rcvd;
				break;
			case REGISTER:
				if (outbound)
					++sip_counters.sip_register_req_sent;
				else
					++sip_counters.sip_register_req_rcvd;
				break;
			case REFER:
				if (outbound)
					++sip_counters.sip_refer_req_sent;
				else
					++sip_counters.sip_refer_req_rcvd;
				break;
			case INFO:
				if (outbound)
					++sip_counters.sip_info_req_sent;
				else
					++sip_counters.sip_info_req_rcvd;
				break;
			case SUBSCRIBE:
				if (outbound)
					++sip_counters.sip_subscribe_req_sent;
				else
					++sip_counters.sip_subscribe_req_rcvd;
				break;
			case NOTIFY:
				if (outbound)
					++sip_counters.sip_notify_req_sent;
				else
					++sip_counters.sip_notify_req_rcvd;
				break;
			case PRACK:
				if (outbound)
					++sip_counters.sip_prack_req_sent;
				else
					++sip_counters.sip_prack_req_rcvd;
				break;
			default:
				break;
		}
	} else {
		if (outbound)
			++sip_counters.sip_total_resp_sent;
		else
			++sip_counters.sip_total_resp_rcvd;
		if (SIP_PROVISIONAL_RESP(resp_code)) {
			if (outbound)
				++sip_counters.sip_1xx_resp_sent;
			else
				++sip_counters.sip_1xx_resp_rcvd;
		} else if (SIP_OK_RESP(resp_code)) {
			if (outbound)
				++sip_counters.sip_2xx_resp_sent;
			else
				++sip_counters.sip_2xx_resp_rcvd;
		} else if (SIP_REDIRECT_RESP(resp_code)) {
			if (outbound)
				++sip_counters.sip_3xx_resp_sent;
			else
				++sip_counters.sip_3xx_resp_rcvd;
		} else if (SIP_REQFAIL_RESP(resp_code)) {
			if (outbound)
				++sip_counters.sip_4xx_resp_sent;
			else
				++sip_counters.sip_4xx_resp_rcvd;
		} else if (SIP_SRVFAIL_RESP(resp_code)) {
			if (outbound)
				++sip_counters.sip_5xx_resp_sent;
			else
				++sip_counters.sip_5xx_resp_rcvd;
		} else if (SIP_GLOBFAIL_RESP(resp_code)) {
			if (outbound)
				++sip_counters.sip_6xx_resp_sent;
			else
				++sip_counters.sip_6xx_resp_rcvd;
		}
	}
}

/*
 * Enables Transaction logging. The flags argument controls the detail
 * of logging.
 */
int
sip_enable_trans_logging(FILE *logfile, int flags)
{
	if (logfile == NULL || flags != SIP_DETAIL_LOGGING)
		return (EINVAL);

	(void) pthread_mutex_lock(&trans_log.sip_logfile_mutex);
	if (!trans_log.sip_logging_enabled) {
		trans_log.sip_logfile = logfile;
		trans_log.sip_logging_enabled = B_TRUE;
	}
	(void) pthread_mutex_unlock(&trans_log.sip_logfile_mutex);
	return (0);
}


/*
 * Enables dialog logging. The flags argument controls the detail
 * of logging.
 */
int
sip_enable_dialog_logging(FILE *logfile, int flags)
{
	if (logfile == NULL || flags != SIP_DETAIL_LOGGING)
		return (EINVAL);

	(void) pthread_mutex_lock(&dialog_log.sip_logfile_mutex);
	if (!dialog_log.sip_logging_enabled) {
		dialog_log.sip_logfile = logfile;
		dialog_log.sip_logging_enabled = B_TRUE;
	}
	(void) pthread_mutex_unlock(&dialog_log.sip_logfile_mutex);
	return (0);
}

void
sip_disable_trans_logging()
{
	(void) pthread_mutex_lock(&trans_log.sip_logfile_mutex);
	if (trans_log.sip_logging_enabled)
		trans_log.sip_logging_enabled = B_FALSE;
	(void) pthread_mutex_unlock(&trans_log.sip_logfile_mutex);
}

void
sip_disable_dialog_logging()
{
	(void) pthread_mutex_lock(&dialog_log.sip_logfile_mutex);
	if (dialog_log.sip_logging_enabled)
		dialog_log.sip_logging_enabled = B_FALSE;
	(void) pthread_mutex_unlock(&dialog_log.sip_logfile_mutex);
}

static void
sip_print_digest(uint16_t *digest, int len, FILE *fp)
{
	int	cnt;

	for (cnt = 0; cnt < len; cnt++)
		(void) fprintf(fp, "%u ", digest[cnt]);
	(void) fprintf(fp, "\n\n");
}

/*
 * Logs all the messages exchanged within a transaction to the transaction
 * log file. Logged messages are then freed.
 */
static void
sip_write_xaction_to_log(void *obj)
{
	sip_xaction_t	*trans = (sip_xaction_t *)obj;
	sip_log_t	*sip_log;
	int		count;
	sip_msg_chain_t	*msg_chain;
	sip_msg_chain_t	*nmsg_chain;
	char		timebuf[TIME_BUF_SIZE];
	struct tm	tms;
	FILE		*sip_trans_logfile = trans_log.sip_logfile;

	assert(trans != NULL && sip_trans_logfile != NULL);
	(void) fprintf(sip_trans_logfile, "************* Begin Transaction"
	    " *************\n");
	(void) fprintf(sip_trans_logfile, "Branchid\t\t: %s\n",
	    trans->sip_xaction_branch_id);
	(void) fprintf(sip_trans_logfile, "Digest\t\t\t: ");
	sip_print_digest(trans->sip_xaction_hash_digest, 8, sip_trans_logfile);
	(void) fprintf(sip_trans_logfile, "-----------------------------\n");
	for (count = 0; count <= SIP_SRV_NONINV_TERMINATED; count++) {
		sip_log = &trans->sip_xaction_log[count];
		if (sip_log->sip_msgcnt == 0)
			continue;
		(void) fprintf(sip_trans_logfile, "Transaction State\t: %s\n\n",
		    sip_get_xaction_state(count));
		msg_chain = sip_log->sip_msgs;
		while (msg_chain != NULL) {
			nmsg_chain = msg_chain->next;
			(void) strftime(timebuf, sizeof (timebuf), NULL,
			    localtime_r(&msg_chain->msg_timestamp, &tms));
			(void) fprintf(sip_trans_logfile, "%s| Message -"
			    " %d\n%s", timebuf, msg_chain->msg_seq, msg_chain->
			    sip_msg);
			free(msg_chain->sip_msg);
			free(msg_chain);
			--sip_log->sip_msgcnt;
			msg_chain = nmsg_chain;
		}
		(void) fprintf(sip_trans_logfile,
		    "-----------------------------\n");
		(trans->sip_xaction_log[count]).sip_msgs = NULL;
	}
	(void) fprintf(sip_trans_logfile, "************* End Transaction "
	    "*************\n");
	(void) fflush(sip_trans_logfile);
}

/*
 * Logs all the messages exchanged within a dialog to the dialog
 * log file. Logged messages are then freed.
 */
static void
sip_write_dlg_to_log(void *obj)
{
	_sip_dialog_t	*dialog = (_sip_dialog_t *)obj;
	sip_log_t	*sip_log;
	int		count;
	sip_msg_chain_t	*msg_chain;
	sip_msg_chain_t	*nmsg_chain;
	char		timebuf[TIME_BUF_SIZE];
	struct tm	tms;
	FILE		*sip_dialog_logfile = dialog_log.sip_logfile;

	assert(dialog != NULL && sip_dialog_logfile != NULL);

	(void) fprintf(sip_dialog_logfile, "************* Begin Dialog "
	    "*************\n");
	(void) fprintf(sip_dialog_logfile, "Digest\t\t\t: ");
	sip_print_digest(dialog->sip_dlg_id, 8, sip_dialog_logfile);
	(void) fprintf(sip_dialog_logfile, "-----------------------------\n");
	for (count = 0; count <= SIP_DLG_DESTROYED; count++) {
		sip_log = &dialog->sip_dlg_log[count];
		if (sip_log->sip_msgcnt == 0)
			continue;
		(void) fprintf(sip_dialog_logfile, "Dialog State\t\t: %s\n\n",
		    sip_get_dialog_state_str(count));
		msg_chain = sip_log->sip_msgs;
		while (msg_chain != NULL) {
			nmsg_chain = msg_chain->next;
			(void) strftime(timebuf, sizeof (timebuf), NULL,
			    localtime_r(&msg_chain->msg_timestamp, &tms));
			(void) fprintf(sip_dialog_logfile, "%s| Message -"
			    " %d\n%s", timebuf, msg_chain->msg_seq, msg_chain->
			    sip_msg);
			free(msg_chain->sip_msg);
			free(msg_chain);
			--sip_log->sip_msgcnt;
			msg_chain = nmsg_chain;
		}
		(void) fprintf(sip_dialog_logfile,
		    "-----------------------------\n");
		(dialog->sip_dlg_log[count]).sip_msgs = NULL;
	}
	(void) fprintf(sip_dialog_logfile, "************* End Dialog "
	    "*************\n");
	(void) fflush(sip_dialog_logfile);
}

/*
 * Calls the appropriate function to log transaction or dialog messages.
 * If this function is called because of assertion failure, then the file and
 * line where the assertion failed is logged to the log file.
 */
void
sip_write_to_log(void *obj, int type, char *file, int line)
{
	if (type & SIP_TRANSACTION_LOG) {
		(void) pthread_mutex_lock(&trans_log.sip_logfile_mutex);
		if (trans_log.sip_logging_enabled) {
			if (type & SIP_ASSERT_ERROR) {
				(void) fprintf(trans_log.sip_logfile,
				    "Assertion Failure at %s:%d\n", file, line);
			}
			sip_write_xaction_to_log(obj);
		}
		(void) pthread_mutex_unlock(&trans_log.sip_logfile_mutex);
	} else {
		(void) pthread_mutex_lock(&dialog_log.sip_logfile_mutex);
		if (dialog_log.sip_logging_enabled) {
			if (type & SIP_ASSERT_ERROR) {
				(void) fprintf(dialog_log.sip_logfile,
				    "Assertion Failure at %s:%d\n", file, line);
			}
			sip_write_dlg_to_log(obj);
		}
		(void) pthread_mutex_unlock(&dialog_log.sip_logfile_mutex);
	}
}

/*
 * This function records the messages that are exchanged within a dialog or
 * transaction. If logging is enabled the recorded messages are then dumped
 * to the log file just before deleting the transaction or dialog.
 */
void
sip_add_log(sip_log_t *sip_log, sip_msg_t sip_msg, int seq, int type)
{
	char			*msgstr;
	sip_msg_chain_t		*new_msg;
	sip_msg_chain_t		*msg_chain = sip_log->sip_msgs;

	/*
	 * No need to take any locks here. Caller of this function MUST
	 * have already taken the transaction or dialog lock.
	 */
	if (((type == SIP_DIALOG_LOG) && !dialog_log.sip_logging_enabled) ||
	    ((type == SIP_TRANSACTION_LOG) && !trans_log.sip_logging_enabled)) {
		return;
	}

	new_msg = calloc(1, sizeof (sip_msg_chain_t));
	if (new_msg == NULL)
		return;

	msgstr = sip_msg_to_str(sip_msg, NULL);
	if (msgstr == NULL) {
		free(new_msg);
		return;
	}

	new_msg->sip_msg =  msgstr;
	new_msg->msg_seq = seq;
	new_msg->msg_timestamp = time(NULL);
	new_msg->next = NULL;
	if (sip_log->sip_msgcnt == 0) {
		sip_log->sip_msgs = new_msg;
	} else {
		while (msg_chain->next != NULL)
			msg_chain = msg_chain->next;
		msg_chain->next = new_msg;
	}
	sip_log->sip_msgcnt++;
}

/*
 * Given a counter group and counter name within the group, returns the value
 * associated with the counter in 'cntval'.
 */
int
sip_get_counter_value(int group, int counter, void *cntval, size_t cntlen)
{
	if (group != SIP_TRAFFIC_COUNTERS || cntval == NULL)
		return (EINVAL);
	if ((counter == SIP_COUNTER_START_TIME || counter ==
	    SIP_COUNTER_STOP_TIME) && (cntlen != sizeof (time_t))) {
		return (EINVAL);
	} else if (cntlen != sizeof (uint64_t)) {
		return (EINVAL);
	}

	(void) pthread_mutex_lock(&sip_counters.sip_counter_mutex);
	switch (counter) {
		case SIP_TOTAL_BYTES_RCVD:
			*(uint64_t *)cntval = sip_counters.sip_total_bytes_rcvd;
			break;
		case SIP_TOTAL_BYTES_SENT:
			*(uint64_t *)cntval = sip_counters.sip_total_bytes_sent;
			break;
		case SIP_TOTAL_REQ_RCVD:
			*(uint64_t *)cntval = sip_counters.sip_total_req_rcvd;
			break;
		case SIP_TOTAL_REQ_SENT:
			*(uint64_t *)cntval = sip_counters.sip_total_req_sent;
			break;
		case SIP_TOTAL_RESP_RCVD:
			*(uint64_t *)cntval = sip_counters.sip_total_resp_rcvd;
			break;
		case SIP_TOTAL_RESP_SENT:
			*(uint64_t *)cntval = sip_counters.sip_total_resp_sent;
			break;
		case SIP_ACK_REQ_RCVD:
			*(uint64_t *)cntval = sip_counters.sip_ack_req_rcvd;
			break;
		case SIP_ACK_REQ_SENT:
			*(uint64_t *)cntval = sip_counters.sip_ack_req_sent;
			break;
		case SIP_BYE_REQ_RCVD:
			*(uint64_t *)cntval = sip_counters.sip_bye_req_rcvd;
			break;
		case SIP_BYE_REQ_SENT:
			*(uint64_t *)cntval = sip_counters.sip_bye_req_sent;
			break;
		case SIP_CANCEL_REQ_RCVD:
			*(uint64_t *)cntval = sip_counters.sip_cancel_req_rcvd;
			break;
		case SIP_CANCEL_REQ_SENT:
			*(uint64_t *)cntval = sip_counters.sip_cancel_req_sent;
			break;
		case SIP_INFO_REQ_RCVD:
			*(uint64_t *)cntval = sip_counters.sip_info_req_rcvd;
			break;
		case SIP_INFO_REQ_SENT:
			*(uint64_t *)cntval = sip_counters.sip_info_req_sent;
			break;
		case SIP_INVITE_REQ_RCVD:
			*(uint64_t *)cntval = sip_counters.sip_invite_req_rcvd;
			break;
		case SIP_INVITE_REQ_SENT:
			*(uint64_t *)cntval = sip_counters.sip_invite_req_sent;
			break;
		case SIP_NOTIFY_REQ_RCVD:
			*(uint64_t *)cntval = sip_counters.sip_notify_req_rcvd;
			break;
		case SIP_NOTIFY_REQ_SENT:
			*(uint64_t *)cntval = sip_counters.sip_notify_req_sent;
			break;
		case SIP_OPTIONS_REQ_RCVD:
			*(uint64_t *)cntval = sip_counters.sip_options_req_rcvd;
			break;
		case SIP_OPTIONS_REQ_SENT:
			*(uint64_t *)cntval = sip_counters.sip_options_req_sent;
			break;
		case SIP_PRACK_REQ_RCVD:
			*(uint64_t *)cntval = sip_counters.sip_prack_req_rcvd;
			break;
		case SIP_PRACK_REQ_SENT:
			*(uint64_t *)cntval = sip_counters.sip_prack_req_sent;
			break;
		case SIP_REFER_REQ_RCVD:
			*(uint64_t *)cntval = sip_counters.sip_refer_req_rcvd;
			break;
		case SIP_REFER_REQ_SENT:
			*(uint64_t *)cntval = sip_counters.sip_refer_req_sent;
			break;
		case SIP_REGISTER_REQ_RCVD:
			*(uint64_t *)cntval = sip_counters.
			    sip_register_req_rcvd;
			break;
		case SIP_REGISTER_REQ_SENT:
			*(uint64_t *)cntval = sip_counters.
			    sip_register_req_sent;
			break;
		case SIP_SUBSCRIBE_REQ_RCVD:
			*(uint64_t *)cntval = sip_counters.
			    sip_subscribe_req_rcvd;
			break;
		case SIP_SUBSCRIBE_REQ_SENT:
			*(uint64_t *)cntval = sip_counters.
			    sip_subscribe_req_sent;
			break;
		case SIP_UPDATE_REQ_RCVD:
			*(uint64_t *)cntval = sip_counters.sip_update_req_rcvd;
			break;
		case SIP_UPDATE_REQ_SENT:
			*(uint64_t *)cntval = sip_counters.sip_update_req_sent;
			break;
		case SIP_1XX_RESP_RCVD:
			*(uint64_t *)cntval = sip_counters.sip_1xx_resp_rcvd;
			break;
		case SIP_1XX_RESP_SENT:
			*(uint64_t *)cntval = sip_counters.sip_1xx_resp_sent;
			break;
		case SIP_2XX_RESP_RCVD:
			*(uint64_t *)cntval = sip_counters.sip_2xx_resp_rcvd;
			break;
		case SIP_2XX_RESP_SENT:
			*(uint64_t *)cntval = sip_counters.sip_2xx_resp_sent;
			break;
		case SIP_3XX_RESP_RCVD:
			*(uint64_t *)cntval = sip_counters.sip_3xx_resp_rcvd;
			break;
		case SIP_3XX_RESP_SENT:
			*(uint64_t *)cntval = sip_counters.sip_3xx_resp_sent;
			break;
		case SIP_4XX_RESP_RCVD:
			*(uint64_t *)cntval = sip_counters.sip_4xx_resp_rcvd;
			break;
		case SIP_4XX_RESP_SENT:
			*(uint64_t *)cntval = sip_counters.sip_4xx_resp_sent;
			break;
		case SIP_5XX_RESP_RCVD:
			*(uint64_t *)cntval = sip_counters.sip_5xx_resp_rcvd;
			break;
		case SIP_5XX_RESP_SENT:
			*(uint64_t *)cntval = sip_counters.sip_5xx_resp_sent;
			break;
		case SIP_6XX_RESP_RCVD:
			*(uint64_t *)cntval = sip_counters.sip_6xx_resp_rcvd;
			break;
		case SIP_6xx_RESP_SENT:
			*(uint64_t *)cntval = sip_counters.sip_6xx_resp_sent;
			break;
		case SIP_COUNTER_START_TIME:
			*(time_t *)cntval = sip_counters.starttime;
			break;
		case SIP_COUNTER_STOP_TIME:
			*(time_t *)cntval = sip_counters.stoptime;
			break;
		default:
			(void) pthread_mutex_unlock(&sip_counters.
			    sip_counter_mutex);
			return (EINVAL);
	}
	(void) pthread_mutex_unlock(&sip_counters.sip_counter_mutex);
	return (0);
}

/*
 * Enables the SIP performance/traffic counting. Also reset's the previous
 * counter values and starts counting afresh.
 */
int
sip_enable_counters(int group)
{
	if (group != SIP_TRAFFIC_COUNTERS)
		return (EINVAL);
	(void) pthread_mutex_lock(&sip_counters.sip_counter_mutex);
	/* If it's not enabled, enable it and capture the start time */
	if (!sip_counters.enabled) {
		/* zero all the counters except for the mutex at the end */
		(void) bzero(&sip_counters, sizeof (sip_traffic_counters_t) -
		    sizeof (pthread_mutex_t));
		sip_counters.enabled = B_TRUE;
		sip_counters.starttime = time(NULL);
		sip_counters.stoptime = 0;
	}
	(void) pthread_mutex_unlock(&sip_counters.sip_counter_mutex);
	return (0);
}

/*
 * Disables the SIP performance/traffic counting. If already disabled it just
 * exits without doing anyting. It records the stop time.
 */
int
sip_disable_counters(int group)
{
	if (group != SIP_TRAFFIC_COUNTERS)
		return (EINVAL);
	(void) pthread_mutex_lock(&sip_counters.sip_counter_mutex);
	if (sip_counters.enabled) {
		sip_counters.enabled = B_FALSE;
		sip_counters.stoptime = time(NULL);
	}
	(void) pthread_mutex_unlock(&sip_counters.sip_counter_mutex);
	return (0);
}