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 */ 52 sip_traffic_counters_t sip_counters; 53 54 /* 55 * Needed for dialog/transaction logging. 56 */ 57 sip_logfile_t trans_log; 58 sip_logfile_t dialog_log; 59 60 /* 61 * This function increments the appropriate inbound/outbound counters for 62 * SIP requests/responses. 63 */ 64 void 65 sip_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 */ 194 int 195 sip_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 */ 214 int 215 sip_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 229 void 230 sip_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 238 void 239 sip_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 247 static void 248 sip_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 */ 261 static void 262 sip_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 */ 313 static void 314 sip_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 */ 365 void 366 sip_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 */ 396 void 397 sip_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 */ 440 int 441 sip_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 */ 603 int 604 sip_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 */ 626 int 627 sip_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