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