1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22/*
23 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27#pragma ident	"%Z%%M%	%I%	%E% SMI"
28
29#include <stdio.h>
30#include <string.h>
31#include <strings.h>
32#include <stdlib.h>
33#include <assert.h>
34#include <ctype.h>
35#include <errno.h>
36#include <sip.h>
37
38#include "sip_msg.h"
39#include "sip_miscdefs.h"
40#include "sip_xaction.h"
41#include "sip_dialog.h"
42
43#define	TIME_BUF_SIZE	50
44
45/*
46 * Contains API's which enable/disable transaction or dialog logging,
47 * API's which records/measures SIP Traffic.
48 */
49/*
50 * Needed for measuring SIP traffic counters.
51 */
52sip_traffic_counters_t	sip_counters;
53
54/*
55 * Needed for dialog/transaction logging.
56 */
57sip_logfile_t trans_log;
58sip_logfile_t dialog_log;
59
60/*
61 * This function increments the appropriate inbound/outbound counters for
62 * SIP requests/responses.
63 */
64void
65sip_measure_traffic(boolean_t is_request, sip_method_t method, int resp_code,
66    boolean_t outbound, int msg_size)
67{
68#ifdef	__solaris__
69	assert(mutex_held(&sip_counters.sip_counter_mutex));
70#endif
71	if (outbound)
72		sip_counters.sip_total_bytes_sent += msg_size;
73	else
74		sip_counters.sip_total_bytes_rcvd += msg_size;
75
76	if (is_request) {
77		if (outbound)
78			++sip_counters.sip_total_req_sent;
79		else
80			++sip_counters.sip_total_req_rcvd;
81		switch (method) {
82			case INVITE:
83				if (outbound)
84					++sip_counters.sip_invite_req_sent;
85				else
86					++sip_counters.sip_invite_req_rcvd;
87				break;
88			case ACK:
89				if (outbound)
90					++sip_counters.sip_ack_req_sent;
91				else
92					++sip_counters.sip_ack_req_rcvd;
93				break;
94			case OPTIONS:
95				if (outbound)
96					++sip_counters.sip_options_req_sent;
97				else
98					++sip_counters.sip_options_req_rcvd;
99				break;
100			case BYE:
101				if (outbound)
102					++sip_counters.sip_bye_req_sent;
103				else
104					++sip_counters.sip_bye_req_rcvd;
105				break;
106			case CANCEL:
107				if (outbound)
108					++sip_counters.sip_cancel_req_sent;
109				else
110					++sip_counters.sip_cancel_req_rcvd;
111				break;
112			case REGISTER:
113				if (outbound)
114					++sip_counters.sip_register_req_sent;
115				else
116					++sip_counters.sip_register_req_rcvd;
117				break;
118			case REFER:
119				if (outbound)
120					++sip_counters.sip_refer_req_sent;
121				else
122					++sip_counters.sip_refer_req_rcvd;
123				break;
124			case INFO:
125				if (outbound)
126					++sip_counters.sip_info_req_sent;
127				else
128					++sip_counters.sip_info_req_rcvd;
129				break;
130			case SUBSCRIBE:
131				if (outbound)
132					++sip_counters.sip_subscribe_req_sent;
133				else
134					++sip_counters.sip_subscribe_req_rcvd;
135				break;
136			case NOTIFY:
137				if (outbound)
138					++sip_counters.sip_notify_req_sent;
139				else
140					++sip_counters.sip_notify_req_rcvd;
141				break;
142			case PRACK:
143				if (outbound)
144					++sip_counters.sip_prack_req_sent;
145				else
146					++sip_counters.sip_prack_req_rcvd;
147				break;
148			default:
149				break;
150		}
151	} else {
152		if (outbound)
153			++sip_counters.sip_total_resp_sent;
154		else
155			++sip_counters.sip_total_resp_rcvd;
156		if (SIP_PROVISIONAL_RESP(resp_code)) {
157			if (outbound)
158				++sip_counters.sip_1xx_resp_sent;
159			else
160				++sip_counters.sip_1xx_resp_rcvd;
161		} else if (SIP_OK_RESP(resp_code)) {
162			if (outbound)
163				++sip_counters.sip_2xx_resp_sent;
164			else
165				++sip_counters.sip_2xx_resp_rcvd;
166		} else if (SIP_REDIRECT_RESP(resp_code)) {
167			if (outbound)
168				++sip_counters.sip_3xx_resp_sent;
169			else
170				++sip_counters.sip_3xx_resp_rcvd;
171		} else if (SIP_REQFAIL_RESP(resp_code)) {
172			if (outbound)
173				++sip_counters.sip_4xx_resp_sent;
174			else
175				++sip_counters.sip_4xx_resp_rcvd;
176		} else if (SIP_SRVFAIL_RESP(resp_code)) {
177			if (outbound)
178				++sip_counters.sip_5xx_resp_sent;
179			else
180				++sip_counters.sip_5xx_resp_rcvd;
181		} else if (SIP_GLOBFAIL_RESP(resp_code)) {
182			if (outbound)
183				++sip_counters.sip_6xx_resp_sent;
184			else
185				++sip_counters.sip_6xx_resp_rcvd;
186		}
187	}
188}
189
190/*
191 * Enables Transaction logging. The flags argument controls the detail
192 * of logging.
193 */
194int
195sip_enable_trans_logging(FILE *logfile, int flags)
196{
197	if (logfile == NULL || flags != SIP_DETAIL_LOGGING)
198		return (EINVAL);
199
200	(void) pthread_mutex_lock(&trans_log.sip_logfile_mutex);
201	if (!trans_log.sip_logging_enabled) {
202		trans_log.sip_logfile = logfile;
203		trans_log.sip_logging_enabled = B_TRUE;
204	}
205	(void) pthread_mutex_unlock(&trans_log.sip_logfile_mutex);
206	return (0);
207}
208
209
210/*
211 * Enables dialog logging. The flags argument controls the detail
212 * of logging.
213 */
214int
215sip_enable_dialog_logging(FILE *logfile, int flags)
216{
217	if (logfile == NULL || flags != SIP_DETAIL_LOGGING)
218		return (EINVAL);
219
220	(void) pthread_mutex_lock(&dialog_log.sip_logfile_mutex);
221	if (!dialog_log.sip_logging_enabled) {
222		dialog_log.sip_logfile = logfile;
223		dialog_log.sip_logging_enabled = B_TRUE;
224	}
225	(void) pthread_mutex_unlock(&dialog_log.sip_logfile_mutex);
226	return (0);
227}
228
229void
230sip_disable_trans_logging()
231{
232	(void) pthread_mutex_lock(&trans_log.sip_logfile_mutex);
233	if (trans_log.sip_logging_enabled)
234		trans_log.sip_logging_enabled = B_FALSE;
235	(void) pthread_mutex_unlock(&trans_log.sip_logfile_mutex);
236}
237
238void
239sip_disable_dialog_logging()
240{
241	(void) pthread_mutex_lock(&dialog_log.sip_logfile_mutex);
242	if (dialog_log.sip_logging_enabled)
243		dialog_log.sip_logging_enabled = B_FALSE;
244	(void) pthread_mutex_unlock(&dialog_log.sip_logfile_mutex);
245}
246
247static void
248sip_print_digest(uint16_t *digest, int len, FILE *fp)
249{
250	int	cnt;
251
252	for (cnt = 0; cnt < len; cnt++)
253		(void) fprintf(fp, "%u ", digest[cnt]);
254	(void) fprintf(fp, "\n\n");
255}
256
257/*
258 * Logs all the messages exchanged within a transaction to the transaction
259 * log file. Logged messages are then freed.
260 */
261static void
262sip_write_xaction_to_log(void *obj)
263{
264	sip_xaction_t	*trans = (sip_xaction_t *)obj;
265	sip_log_t	*sip_log;
266	int		count;
267	sip_msg_chain_t	*msg_chain;
268	sip_msg_chain_t	*nmsg_chain;
269	char		timebuf[TIME_BUF_SIZE];
270	struct tm	tms;
271	FILE		*sip_trans_logfile = trans_log.sip_logfile;
272
273	assert(trans != NULL && sip_trans_logfile != NULL);
274	(void) fprintf(sip_trans_logfile, "************* Begin Transaction"
275	    " *************\n");
276	(void) fprintf(sip_trans_logfile, "Branchid\t\t: %s\n",
277	    trans->sip_xaction_branch_id);
278	(void) fprintf(sip_trans_logfile, "Digest\t\t\t: ");
279	sip_print_digest(trans->sip_xaction_hash_digest, 8, sip_trans_logfile);
280	(void) fprintf(sip_trans_logfile, "-----------------------------\n");
281	for (count = 0; count <= SIP_SRV_NONINV_TERMINATED; count++) {
282		sip_log = &trans->sip_xaction_log[count];
283		if (sip_log->sip_msgcnt == 0)
284			continue;
285		(void) fprintf(sip_trans_logfile, "Transaction State\t: %s\n\n",
286		    sip_get_xaction_state(count));
287		msg_chain = sip_log->sip_msgs;
288		while (msg_chain != NULL) {
289			nmsg_chain = msg_chain->next;
290			(void) strftime(timebuf, sizeof (timebuf), NULL,
291			    localtime_r(&msg_chain->msg_timestamp, &tms));
292			(void) fprintf(sip_trans_logfile, "%s| Message -"
293			    " %d\n%s", timebuf, msg_chain->msg_seq, msg_chain->
294			    sip_msg);
295			free(msg_chain->sip_msg);
296			free(msg_chain);
297			--sip_log->sip_msgcnt;
298			msg_chain = nmsg_chain;
299		}
300		(void) fprintf(sip_trans_logfile,
301		    "-----------------------------\n");
302		(trans->sip_xaction_log[count]).sip_msgs = NULL;
303	}
304	(void) fprintf(sip_trans_logfile, "************* End Transaction "
305	    "*************\n");
306	(void) fflush(sip_trans_logfile);
307}
308
309/*
310 * Logs all the messages exchanged within a dialog to the dialog
311 * log file. Logged messages are then freed.
312 */
313static void
314sip_write_dlg_to_log(void *obj)
315{
316	_sip_dialog_t	*dialog = (_sip_dialog_t *)obj;
317	sip_log_t	*sip_log;
318	int		count;
319	sip_msg_chain_t	*msg_chain;
320	sip_msg_chain_t	*nmsg_chain;
321	char		timebuf[TIME_BUF_SIZE];
322	struct tm	tms;
323	FILE		*sip_dialog_logfile = dialog_log.sip_logfile;
324
325	assert(dialog != NULL && sip_dialog_logfile != NULL);
326
327	(void) fprintf(sip_dialog_logfile, "************* Begin Dialog "
328	    "*************\n");
329	(void) fprintf(sip_dialog_logfile, "Digest\t\t\t: ");
330	sip_print_digest(dialog->sip_dlg_id, 8, sip_dialog_logfile);
331	(void) fprintf(sip_dialog_logfile, "-----------------------------\n");
332	for (count = 0; count <= SIP_DLG_DESTROYED; count++) {
333		sip_log = &dialog->sip_dlg_log[count];
334		if (sip_log->sip_msgcnt == 0)
335			continue;
336		(void) fprintf(sip_dialog_logfile, "Dialog State\t\t: %s\n\n",
337		    sip_get_dialog_state_str(count));
338		msg_chain = sip_log->sip_msgs;
339		while (msg_chain != NULL) {
340			nmsg_chain = msg_chain->next;
341			(void) strftime(timebuf, sizeof (timebuf), NULL,
342			    localtime_r(&msg_chain->msg_timestamp, &tms));
343			(void) fprintf(sip_dialog_logfile, "%s| Message -"
344			    " %d\n%s", timebuf, msg_chain->msg_seq, msg_chain->
345			    sip_msg);
346			free(msg_chain->sip_msg);
347			free(msg_chain);
348			--sip_log->sip_msgcnt;
349			msg_chain = nmsg_chain;
350		}
351		(void) fprintf(sip_dialog_logfile,
352		    "-----------------------------\n");
353		(dialog->sip_dlg_log[count]).sip_msgs = NULL;
354	}
355	(void) fprintf(sip_dialog_logfile, "************* End Dialog "
356	    "*************\n");
357	(void) fflush(sip_dialog_logfile);
358}
359
360/*
361 * Calls the appropriate function to log transaction or dialog messages.
362 * If this function is called because of assertion failure, then the file and
363 * line where the assertion failed is logged to the log file.
364 */
365void
366sip_write_to_log(void *obj, int type, char *file, int line)
367{
368	if (type & SIP_TRANSACTION_LOG) {
369		(void) pthread_mutex_lock(&trans_log.sip_logfile_mutex);
370		if (trans_log.sip_logging_enabled) {
371			if (type & SIP_ASSERT_ERROR) {
372				(void) fprintf(trans_log.sip_logfile,
373				    "Assertion Failure at %s:%d\n", file, line);
374			}
375			sip_write_xaction_to_log(obj);
376		}
377		(void) pthread_mutex_unlock(&trans_log.sip_logfile_mutex);
378	} else {
379		(void) pthread_mutex_lock(&dialog_log.sip_logfile_mutex);
380		if (dialog_log.sip_logging_enabled) {
381			if (type & SIP_ASSERT_ERROR) {
382				(void) fprintf(dialog_log.sip_logfile,
383				    "Assertion Failure at %s:%d\n", file, line);
384			}
385			sip_write_dlg_to_log(obj);
386		}
387		(void) pthread_mutex_unlock(&dialog_log.sip_logfile_mutex);
388	}
389}
390
391/*
392 * This function records the messages that are exchanged within a dialog or
393 * transaction. If logging is enabled the recorded messages are then dumped
394 * to the log file just before deleting the transaction or dialog.
395 */
396void
397sip_add_log(sip_log_t *sip_log, sip_msg_t sip_msg, int seq, int type)
398{
399	char			*msgstr;
400	sip_msg_chain_t		*new_msg;
401	sip_msg_chain_t		*msg_chain = sip_log->sip_msgs;
402
403	/*
404	 * No need to take any locks here. Caller of this function MUST
405	 * have already taken the transaction or dialog lock.
406	 */
407	if (((type == SIP_DIALOG_LOG) && !dialog_log.sip_logging_enabled) ||
408	    ((type == SIP_TRANSACTION_LOG) && !trans_log.sip_logging_enabled)) {
409		return;
410	}
411
412	new_msg = calloc(1, sizeof (sip_msg_chain_t));
413	if (new_msg == NULL)
414		return;
415
416	msgstr = sip_msg_to_str(sip_msg, NULL);
417	if (msgstr == NULL) {
418		free(new_msg);
419		return;
420	}
421
422	new_msg->sip_msg =  msgstr;
423	new_msg->msg_seq = seq;
424	new_msg->msg_timestamp = time(NULL);
425	new_msg->next = NULL;
426	if (sip_log->sip_msgcnt == 0) {
427		sip_log->sip_msgs = new_msg;
428	} else {
429		while (msg_chain->next != NULL)
430			msg_chain = msg_chain->next;
431		msg_chain->next = new_msg;
432	}
433	sip_log->sip_msgcnt++;
434}
435
436/*
437 * Given a counter group and counter name within the group, returns the value
438 * associated with the counter in 'cntval'.
439 */
440int
441sip_get_counter_value(int group, int counter, void *cntval, size_t cntlen)
442{
443	if (group != SIP_TRAFFIC_COUNTERS || cntval == NULL)
444		return (EINVAL);
445	if ((counter == SIP_COUNTER_START_TIME || counter ==
446	    SIP_COUNTER_STOP_TIME) && (cntlen != sizeof (time_t))) {
447		return (EINVAL);
448	} else if (cntlen != sizeof (uint64_t)) {
449		return (EINVAL);
450	}
451
452	(void) pthread_mutex_lock(&sip_counters.sip_counter_mutex);
453	switch (counter) {
454		case SIP_TOTAL_BYTES_RCVD:
455			*(uint64_t *)cntval = sip_counters.sip_total_bytes_rcvd;
456			break;
457		case SIP_TOTAL_BYTES_SENT:
458			*(uint64_t *)cntval = sip_counters.sip_total_bytes_sent;
459			break;
460		case SIP_TOTAL_REQ_RCVD:
461			*(uint64_t *)cntval = sip_counters.sip_total_req_rcvd;
462			break;
463		case SIP_TOTAL_REQ_SENT:
464			*(uint64_t *)cntval = sip_counters.sip_total_req_sent;
465			break;
466		case SIP_TOTAL_RESP_RCVD:
467			*(uint64_t *)cntval = sip_counters.sip_total_resp_rcvd;
468			break;
469		case SIP_TOTAL_RESP_SENT:
470			*(uint64_t *)cntval = sip_counters.sip_total_resp_sent;
471			break;
472		case SIP_ACK_REQ_RCVD:
473			*(uint64_t *)cntval = sip_counters.sip_ack_req_rcvd;
474			break;
475		case SIP_ACK_REQ_SENT:
476			*(uint64_t *)cntval = sip_counters.sip_ack_req_sent;
477			break;
478		case SIP_BYE_REQ_RCVD:
479			*(uint64_t *)cntval = sip_counters.sip_bye_req_rcvd;
480			break;
481		case SIP_BYE_REQ_SENT:
482			*(uint64_t *)cntval = sip_counters.sip_bye_req_sent;
483			break;
484		case SIP_CANCEL_REQ_RCVD:
485			*(uint64_t *)cntval = sip_counters.sip_cancel_req_rcvd;
486			break;
487		case SIP_CANCEL_REQ_SENT:
488			*(uint64_t *)cntval = sip_counters.sip_cancel_req_sent;
489			break;
490		case SIP_INFO_REQ_RCVD:
491			*(uint64_t *)cntval = sip_counters.sip_info_req_rcvd;
492			break;
493		case SIP_INFO_REQ_SENT:
494			*(uint64_t *)cntval = sip_counters.sip_info_req_sent;
495			break;
496		case SIP_INVITE_REQ_RCVD:
497			*(uint64_t *)cntval = sip_counters.sip_invite_req_rcvd;
498			break;
499		case SIP_INVITE_REQ_SENT:
500			*(uint64_t *)cntval = sip_counters.sip_invite_req_sent;
501			break;
502		case SIP_NOTIFY_REQ_RCVD:
503			*(uint64_t *)cntval = sip_counters.sip_notify_req_rcvd;
504			break;
505		case SIP_NOTIFY_REQ_SENT:
506			*(uint64_t *)cntval = sip_counters.sip_notify_req_sent;
507			break;
508		case SIP_OPTIONS_REQ_RCVD:
509			*(uint64_t *)cntval = sip_counters.sip_options_req_rcvd;
510			break;
511		case SIP_OPTIONS_REQ_SENT:
512			*(uint64_t *)cntval = sip_counters.sip_options_req_sent;
513			break;
514		case SIP_PRACK_REQ_RCVD:
515			*(uint64_t *)cntval = sip_counters.sip_prack_req_rcvd;
516			break;
517		case SIP_PRACK_REQ_SENT:
518			*(uint64_t *)cntval = sip_counters.sip_prack_req_sent;
519			break;
520		case SIP_REFER_REQ_RCVD:
521			*(uint64_t *)cntval = sip_counters.sip_refer_req_rcvd;
522			break;
523		case SIP_REFER_REQ_SENT:
524			*(uint64_t *)cntval = sip_counters.sip_refer_req_sent;
525			break;
526		case SIP_REGISTER_REQ_RCVD:
527			*(uint64_t *)cntval = sip_counters.
528			    sip_register_req_rcvd;
529			break;
530		case SIP_REGISTER_REQ_SENT:
531			*(uint64_t *)cntval = sip_counters.
532			    sip_register_req_sent;
533			break;
534		case SIP_SUBSCRIBE_REQ_RCVD:
535			*(uint64_t *)cntval = sip_counters.
536			    sip_subscribe_req_rcvd;
537			break;
538		case SIP_SUBSCRIBE_REQ_SENT:
539			*(uint64_t *)cntval = sip_counters.
540			    sip_subscribe_req_sent;
541			break;
542		case SIP_UPDATE_REQ_RCVD:
543			*(uint64_t *)cntval = sip_counters.sip_update_req_rcvd;
544			break;
545		case SIP_UPDATE_REQ_SENT:
546			*(uint64_t *)cntval = sip_counters.sip_update_req_sent;
547			break;
548		case SIP_1XX_RESP_RCVD:
549			*(uint64_t *)cntval = sip_counters.sip_1xx_resp_rcvd;
550			break;
551		case SIP_1XX_RESP_SENT:
552			*(uint64_t *)cntval = sip_counters.sip_1xx_resp_sent;
553			break;
554		case SIP_2XX_RESP_RCVD:
555			*(uint64_t *)cntval = sip_counters.sip_2xx_resp_rcvd;
556			break;
557		case SIP_2XX_RESP_SENT:
558			*(uint64_t *)cntval = sip_counters.sip_2xx_resp_sent;
559			break;
560		case SIP_3XX_RESP_RCVD:
561			*(uint64_t *)cntval = sip_counters.sip_3xx_resp_rcvd;
562			break;
563		case SIP_3XX_RESP_SENT:
564			*(uint64_t *)cntval = sip_counters.sip_3xx_resp_sent;
565			break;
566		case SIP_4XX_RESP_RCVD:
567			*(uint64_t *)cntval = sip_counters.sip_4xx_resp_rcvd;
568			break;
569		case SIP_4XX_RESP_SENT:
570			*(uint64_t *)cntval = sip_counters.sip_4xx_resp_sent;
571			break;
572		case SIP_5XX_RESP_RCVD:
573			*(uint64_t *)cntval = sip_counters.sip_5xx_resp_rcvd;
574			break;
575		case SIP_5XX_RESP_SENT:
576			*(uint64_t *)cntval = sip_counters.sip_5xx_resp_sent;
577			break;
578		case SIP_6XX_RESP_RCVD:
579			*(uint64_t *)cntval = sip_counters.sip_6xx_resp_rcvd;
580			break;
581		case SIP_6xx_RESP_SENT:
582			*(uint64_t *)cntval = sip_counters.sip_6xx_resp_sent;
583			break;
584		case SIP_COUNTER_START_TIME:
585			*(time_t *)cntval = sip_counters.starttime;
586			break;
587		case SIP_COUNTER_STOP_TIME:
588			*(time_t *)cntval = sip_counters.stoptime;
589			break;
590		default:
591			(void) pthread_mutex_unlock(&sip_counters.
592			    sip_counter_mutex);
593			return (EINVAL);
594	}
595	(void) pthread_mutex_unlock(&sip_counters.sip_counter_mutex);
596	return (0);
597}
598
599/*
600 * Enables the SIP performance/traffic counting. Also reset's the previous
601 * counter values and starts counting afresh.
602 */
603int
604sip_enable_counters(int group)
605{
606	if (group != SIP_TRAFFIC_COUNTERS)
607		return (EINVAL);
608	(void) pthread_mutex_lock(&sip_counters.sip_counter_mutex);
609	/* If it's not enabled, enable it and capture the start time */
610	if (!sip_counters.enabled) {
611		/* zero all the counters except for the mutex at the end */
612		(void) bzero(&sip_counters, sizeof (sip_traffic_counters_t) -
613		    sizeof (pthread_mutex_t));
614		sip_counters.enabled = B_TRUE;
615		sip_counters.starttime = time(NULL);
616		sip_counters.stoptime = 0;
617	}
618	(void) pthread_mutex_unlock(&sip_counters.sip_counter_mutex);
619	return (0);
620}
621
622/*
623 * Disables the SIP performance/traffic counting. If already disabled it just
624 * exits without doing anyting. It records the stop time.
625 */
626int
627sip_disable_counters(int group)
628{
629	if (group != SIP_TRAFFIC_COUNTERS)
630		return (EINVAL);
631	(void) pthread_mutex_lock(&sip_counters.sip_counter_mutex);
632	if (sip_counters.enabled) {
633		sip_counters.enabled = B_FALSE;
634		sip_counters.stoptime = time(NULL);
635	}
636	(void) pthread_mutex_unlock(&sip_counters.sip_counter_mutex);
637	return (0);
638}
639