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 2007 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* 28 * etm.c FMA Event Transport Module implementation, a plugin of FMD 29 * for sun4v/Ontario 30 * 31 * plugin for sending/receiving FMA events to/from service processor 32 */ 33 34 #pragma ident "%Z%%M% %I% %E% SMI" 35 36 /* 37 * --------------------------------- includes -------------------------------- 38 */ 39 40 #include <sys/fm/protocol.h> 41 #include <sys/fm/util.h> 42 #include <sys/fm/ldom.h> 43 #include <sys/strlog.h> 44 #include <sys/syslog.h> 45 #include <netinet/in.h> 46 #include <fm/fmd_api.h> 47 48 #include "etm_xport_api.h" 49 #include "etm_etm_proto.h" 50 #include "etm_impl.h" 51 52 #include <pthread.h> 53 #include <signal.h> 54 #include <stropts.h> 55 #include <locale.h> 56 #include <strings.h> 57 #include <stdlib.h> 58 #include <unistd.h> 59 #include <limits.h> 60 #include <values.h> 61 #include <alloca.h> 62 #include <errno.h> 63 #include <fcntl.h> 64 #include <time.h> 65 66 /* 67 * ----------------------------- forward decls ------------------------------- 68 */ 69 70 static void 71 etm_recv(fmd_hdl_t *hdl, fmd_event_t *ep, nvlist_t *nvl, const char *class); 72 73 /* 74 * ------------------------- data structs for FMD ---------------------------- 75 */ 76 77 static const fmd_hdl_ops_t fmd_ops = { 78 etm_recv, /* fmdo_recv */ 79 NULL, /* fmdo_timeout */ 80 NULL, /* fmdo_close */ 81 NULL, /* fmdo_stats */ 82 NULL, /* fmdo_gc */ 83 NULL, /* fmdo_send */ 84 }; 85 86 static const fmd_prop_t fmd_props[] = { 87 { ETM_PROP_NM_XPORT_ADDRS, FMD_TYPE_STRING, "" }, 88 { ETM_PROP_NM_DEBUG_LVL, FMD_TYPE_INT32, "0" }, 89 { ETM_PROP_NM_DEBUG_MAX_EV_CNT, FMD_TYPE_INT32, "-1" }, 90 { ETM_PROP_NM_CONSOLE, FMD_TYPE_BOOL, "false" }, 91 { ETM_PROP_NM_SYSLOGD, FMD_TYPE_BOOL, "true" }, 92 { ETM_PROP_NM_FACILITY, FMD_TYPE_STRING, "LOG_DAEMON" }, 93 { ETM_PROP_NM_MAX_RESP_Q_LEN, FMD_TYPE_INT32, "512" }, 94 { NULL, 0, NULL } 95 }; 96 97 98 static const fmd_hdl_info_t fmd_info = { 99 "FMA Event Transport Module", "1.1", &fmd_ops, fmd_props 100 }; 101 102 /* 103 * ----------------------- private consts and defns -------------------------- 104 */ 105 106 /* misc buffer for variable sized protocol header fields */ 107 108 #define ETM_MISC_BUF_SZ (4 * 1024) 109 110 /* try limit for IO operations w/ capped exp backoff sleep on retry */ 111 112 /* 113 * Design_Note: ETM will potentially retry forever IO operations that the 114 * transport fails with EAGAIN (aka EWOULDBLOCK) rather than 115 * giving up after some number of seconds. This avoids 116 * dropping FMA events while the service processor is down, 117 * but at the risk of pending fmdo_recv() forever and 118 * overflowing FMD's event queue for ETM. 119 * A future TBD enhancement would be to always recv 120 * and send each ETM msg in a single read/write() to reduce 121 * the risk of failure between ETM msg hdr and body, 122 * assuming the MTU_SZ is large enough. 123 */ 124 125 #define ETM_TRY_MAX_CNT (MAXINT - 1) 126 #define ETM_TRY_BACKOFF_RATE (4) 127 #define ETM_TRY_BACKOFF_CAP (60) 128 129 /* amount to increment protocol transaction id on each new send */ 130 131 #define ETM_XID_INC (2) 132 133 typedef struct etm_resp_q_ele { 134 135 etm_xport_conn_t rqe_conn; /* open connection to send on */ 136 etm_proto_v1_pp_t *rqe_hdrp; /* ptr to ETM msg hdr */ 137 size_t rqe_hdr_sz; /* sizeof ETM msg hdr */ 138 int32_t rqe_resp_code; /* response code to send */ 139 140 struct etm_resp_q_ele *rqe_nextp; /* PRIVATE - next ele ptr */ 141 142 } etm_resp_q_ele_t; /* responder queue element */ 143 144 /* 145 * ---------------------------- global data ---------------------------------- 146 */ 147 148 static fmd_hdl_t 149 *init_hdl = NULL; /* used in mem allocator at init time */ 150 151 static int 152 etm_debug_lvl = 0; /* debug level: 0 is off, 1 is on, 2 is more, etc */ 153 154 static int 155 etm_debug_max_ev_cnt = -1; /* max allowed event count for debugging */ 156 157 static fmd_xprt_t 158 *etm_fmd_xprt = NULL; /* FMD transport layer handle */ 159 160 static pthread_t 161 etm_svr_tid = NULL; /* thread id of connection acceptance server */ 162 163 static pthread_t 164 etm_resp_tid = NULL; /* thread id of msg responder */ 165 166 static etm_resp_q_ele_t 167 *etm_resp_q_head = NULL; /* ptr to cur head of responder queue */ 168 169 static etm_resp_q_ele_t 170 *etm_resp_q_tail = NULL; /* ptr to cur tail of responder queue */ 171 172 static uint32_t 173 etm_resp_q_cur_len = 0; /* cur length (ele cnt) of responder queue */ 174 175 static uint32_t 176 etm_resp_q_max_len = 0; /* max length (ele cnt) of responder queue */ 177 178 static pthread_mutex_t 179 etm_resp_q_lock = PTHREAD_MUTEX_INITIALIZER; /* protects responder queue */ 180 181 static pthread_cond_t 182 etm_resp_q_cv = PTHREAD_COND_INITIALIZER; /* nudges msg responder */ 183 184 static volatile int 185 etm_is_dying = 0; /* bool for dying (killing self) */ 186 187 static uint32_t 188 etm_xid_cur = 0; /* current transaction id for sends */ 189 190 static uint32_t 191 etm_xid_ping = 0; /* xid of last CONTROL msg sent requesting ping */ 192 193 static uint32_t 194 etm_xid_ver_negot = 0; /* xid of last CONTROL msg sent requesting ver negot */ 195 196 static uint32_t 197 etm_xid_posted_ev = 0; /* xid of last FMA_EVENT msg/event posted OK to FMD */ 198 199 static uint32_t 200 etm_xid_posted_sa = 0; /* xid of last ALERT msg/event posted OK to syslog */ 201 202 static uint8_t 203 etm_resp_ver = ETM_PROTO_V1; /* proto ver [negotiated] for msg sends */ 204 205 static pthread_mutex_t 206 etm_write_lock = PTHREAD_MUTEX_INITIALIZER; /* for write operations */ 207 208 static log_ctl_t syslog_ctl; /* log(7D) meta-data for each msg */ 209 static int syslog_facility; /* log(7D) facility (part of priority) */ 210 static int syslog_logfd = -1; /* log(7D) file descriptor */ 211 static int syslog_msgfd = -1; /* sysmsg(7D) file descriptor */ 212 static int syslog_file = 0; /* log to syslog_logfd */ 213 static int syslog_cons = 0; /* log to syslog_msgfd */ 214 215 static const struct facility { 216 const char *fac_name; 217 int fac_value; 218 } syslog_facs[] = { 219 { "LOG_DAEMON", LOG_DAEMON }, 220 { "LOG_LOCAL0", LOG_LOCAL0 }, 221 { "LOG_LOCAL1", LOG_LOCAL1 }, 222 { "LOG_LOCAL2", LOG_LOCAL2 }, 223 { "LOG_LOCAL3", LOG_LOCAL3 }, 224 { "LOG_LOCAL4", LOG_LOCAL4 }, 225 { "LOG_LOCAL5", LOG_LOCAL5 }, 226 { "LOG_LOCAL6", LOG_LOCAL6 }, 227 { "LOG_LOCAL7", LOG_LOCAL7 }, 228 { NULL, 0 } 229 }; 230 231 static struct stats { 232 233 /* ETM msg counters */ 234 235 fmd_stat_t etm_rd_hdr_fmaevent; 236 fmd_stat_t etm_rd_hdr_control; 237 fmd_stat_t etm_rd_hdr_alert; 238 fmd_stat_t etm_rd_hdr_response; 239 fmd_stat_t etm_rd_body_fmaevent; 240 fmd_stat_t etm_rd_body_control; 241 fmd_stat_t etm_rd_body_alert; 242 fmd_stat_t etm_rd_body_response; 243 fmd_stat_t etm_wr_hdr_fmaevent; 244 fmd_stat_t etm_wr_hdr_control; 245 fmd_stat_t etm_wr_hdr_response; 246 fmd_stat_t etm_wr_body_fmaevent; 247 fmd_stat_t etm_wr_body_control; 248 fmd_stat_t etm_wr_body_response; 249 250 fmd_stat_t etm_rd_max_ev_per_msg; 251 fmd_stat_t etm_wr_max_ev_per_msg; 252 253 fmd_stat_t etm_resp_q_cur_len; 254 fmd_stat_t etm_resp_q_max_len; 255 256 /* ETM byte counters */ 257 258 fmd_stat_t etm_wr_fmd_bytes; 259 fmd_stat_t etm_rd_fmd_bytes; 260 fmd_stat_t etm_wr_xport_bytes; 261 fmd_stat_t etm_rd_xport_bytes; 262 263 fmd_stat_t etm_magic_drop_bytes; 264 265 /* ETM [dropped] FMA event counters */ 266 267 fmd_stat_t etm_rd_fmd_fmaevent; 268 fmd_stat_t etm_wr_fmd_fmaevent; 269 270 fmd_stat_t etm_rd_drop_fmaevent; 271 fmd_stat_t etm_wr_drop_fmaevent; 272 273 fmd_stat_t etm_rd_dup_fmaevent; 274 fmd_stat_t etm_wr_dup_fmaevent; 275 276 fmd_stat_t etm_rd_dup_alert; 277 fmd_stat_t etm_wr_dup_alert; 278 279 fmd_stat_t etm_enq_drop_resp_q; 280 fmd_stat_t etm_deq_drop_resp_q; 281 282 /* ETM protocol failures */ 283 284 fmd_stat_t etm_magic_bad; 285 fmd_stat_t etm_ver_bad; 286 fmd_stat_t etm_msgtype_bad; 287 fmd_stat_t etm_subtype_bad; 288 fmd_stat_t etm_xid_bad; 289 fmd_stat_t etm_fmaeventlen_bad; 290 fmd_stat_t etm_respcode_bad; 291 fmd_stat_t etm_timeout_bad; 292 fmd_stat_t etm_evlens_bad; 293 294 /* IO operation failures */ 295 296 fmd_stat_t etm_xport_wr_fail; 297 fmd_stat_t etm_xport_rd_fail; 298 fmd_stat_t etm_xport_pk_fail; 299 300 /* IO operation retries */ 301 302 fmd_stat_t etm_xport_wr_retry; 303 fmd_stat_t etm_xport_rd_retry; 304 fmd_stat_t etm_xport_pk_retry; 305 306 /* system and library failures */ 307 308 fmd_stat_t etm_os_nvlist_pack_fail; 309 fmd_stat_t etm_os_nvlist_unpack_fail; 310 fmd_stat_t etm_os_nvlist_size_fail; 311 fmd_stat_t etm_os_pthread_create_fail; 312 313 /* xport API failures */ 314 315 fmd_stat_t etm_xport_get_ev_addrv_fail; 316 fmd_stat_t etm_xport_open_fail; 317 fmd_stat_t etm_xport_close_fail; 318 fmd_stat_t etm_xport_accept_fail; 319 fmd_stat_t etm_xport_open_retry; 320 321 /* FMD entry point bad arguments */ 322 323 fmd_stat_t etm_fmd_recv_badargs; 324 fmd_stat_t etm_fmd_init_badargs; 325 fmd_stat_t etm_fmd_fini_badargs; 326 327 /* Alert logging errors */ 328 329 fmd_stat_t etm_log_err; 330 fmd_stat_t etm_msg_err; 331 332 } etm_stats = { 333 334 /* ETM msg counters */ 335 336 { "etm_rd_hdr_fmaevent", FMD_TYPE_UINT64, 337 "ETM fmaevent msg headers rcvd from xport" }, 338 { "etm_rd_hdr_control", FMD_TYPE_UINT64, 339 "ETM control msg headers rcvd from xport" }, 340 { "etm_rd_hdr_alert", FMD_TYPE_UINT64, 341 "ETM alert msg headers rcvd from xport" }, 342 { "etm_rd_hdr_response", FMD_TYPE_UINT64, 343 "ETM response msg headers rcvd from xport" }, 344 { "etm_rd_body_fmaevent", FMD_TYPE_UINT64, 345 "ETM fmaevent msg bodies rcvd from xport" }, 346 { "etm_rd_body_control", FMD_TYPE_UINT64, 347 "ETM control msg bodies rcvd from xport" }, 348 { "etm_rd_body_alert", FMD_TYPE_UINT64, 349 "ETM alert msg bodies rcvd from xport" }, 350 { "etm_rd_body_response", FMD_TYPE_UINT64, 351 "ETM response msg bodies rcvd from xport" }, 352 { "etm_wr_hdr_fmaevent", FMD_TYPE_UINT64, 353 "ETM fmaevent msg headers sent to xport" }, 354 { "etm_wr_hdr_control", FMD_TYPE_UINT64, 355 "ETM control msg headers sent to xport" }, 356 { "etm_wr_hdr_response", FMD_TYPE_UINT64, 357 "ETM response msg headers sent to xport" }, 358 { "etm_wr_body_fmaevent", FMD_TYPE_UINT64, 359 "ETM fmaevent msg bodies sent to xport" }, 360 { "etm_wr_body_control", FMD_TYPE_UINT64, 361 "ETM control msg bodies sent to xport" }, 362 { "etm_wr_body_response", FMD_TYPE_UINT64, 363 "ETM response msg bodies sent to xport" }, 364 365 { "etm_rd_max_ev_per_msg", FMD_TYPE_UINT64, 366 "max FMA events per ETM msg from xport" }, 367 { "etm_wr_max_ev_per_msg", FMD_TYPE_UINT64, 368 "max FMA events per ETM msg to xport" }, 369 370 { "etm_resp_q_cur_len", FMD_TYPE_UINT64, 371 "cur enqueued response msgs to xport" }, 372 { "etm_resp_q_max_len", FMD_TYPE_UINT64, 373 "max enqueable response msgs to xport" }, 374 375 /* ETM byte counters */ 376 377 { "etm_wr_fmd_bytes", FMD_TYPE_UINT64, 378 "bytes of FMA events sent to FMD" }, 379 { "etm_rd_fmd_bytes", FMD_TYPE_UINT64, 380 "bytes of FMA events rcvd from FMD" }, 381 { "etm_wr_xport_bytes", FMD_TYPE_UINT64, 382 "bytes of FMA events sent to xport" }, 383 { "etm_rd_xport_bytes", FMD_TYPE_UINT64, 384 "bytes of FMA events rcvd from xport" }, 385 386 { "etm_magic_drop_bytes", FMD_TYPE_UINT64, 387 "bytes dropped from xport pre magic num" }, 388 389 /* ETM [dropped] FMA event counters */ 390 391 { "etm_rd_fmd_fmaevent", FMD_TYPE_UINT64, 392 "FMA events rcvd from FMD" }, 393 { "etm_wr_fmd_fmaevent", FMD_TYPE_UINT64, 394 "FMA events sent to FMD" }, 395 396 { "etm_rd_drop_fmaevent", FMD_TYPE_UINT64, 397 "dropped FMA events from xport" }, 398 { "etm_wr_drop_fmaevent", FMD_TYPE_UINT64, 399 "dropped FMA events to xport" }, 400 401 { "etm_rd_dup_fmaevent", FMD_TYPE_UINT64, 402 "duplicate FMA events rcvd from xport" }, 403 { "etm_wr_dup_fmaevent", FMD_TYPE_UINT64, 404 "duplicate FMA events sent to xport" }, 405 406 { "etm_rd_dup_alert", FMD_TYPE_UINT64, 407 "duplicate ALERTs rcvd from xport" }, 408 { "etm_wr_dup_alert", FMD_TYPE_UINT64, 409 "duplicate ALERTs sent to xport" }, 410 411 { "etm_enq_drop_resp_q", FMD_TYPE_UINT64, 412 "dropped response msgs on enq" }, 413 { "etm_deq_drop_resp_q", FMD_TYPE_UINT64, 414 "dropped response msgs on deq" }, 415 416 /* ETM protocol failures */ 417 418 { "etm_magic_bad", FMD_TYPE_UINT64, 419 "ETM msgs w/ invalid magic num" }, 420 { "etm_ver_bad", FMD_TYPE_UINT64, 421 "ETM msgs w/ invalid protocol version" }, 422 { "etm_msgtype_bad", FMD_TYPE_UINT64, 423 "ETM msgs w/ invalid message type" }, 424 { "etm_subtype_bad", FMD_TYPE_UINT64, 425 "ETM msgs w/ invalid sub type" }, 426 { "etm_xid_bad", FMD_TYPE_UINT64, 427 "ETM msgs w/ unmatched xid" }, 428 { "etm_fmaeventlen_bad", FMD_TYPE_UINT64, 429 "ETM msgs w/ invalid FMA event length" }, 430 { "etm_respcode_bad", FMD_TYPE_UINT64, 431 "ETM msgs w/ invalid response code" }, 432 { "etm_timeout_bad", FMD_TYPE_UINT64, 433 "ETM msgs w/ invalid timeout value" }, 434 { "etm_evlens_bad", FMD_TYPE_UINT64, 435 "ETM msgs w/ too many event lengths" }, 436 437 /* IO operation failures */ 438 439 { "etm_xport_wr_fail", FMD_TYPE_UINT64, 440 "xport write failures" }, 441 { "etm_xport_rd_fail", FMD_TYPE_UINT64, 442 "xport read failures" }, 443 { "etm_xport_pk_fail", FMD_TYPE_UINT64, 444 "xport peek failures" }, 445 446 /* IO operation retries */ 447 448 { "etm_xport_wr_retry", FMD_TYPE_UINT64, 449 "xport write retries" }, 450 { "etm_xport_rd_retry", FMD_TYPE_UINT64, 451 "xport read retries" }, 452 { "etm_xport_pk_retry", FMD_TYPE_UINT64, 453 "xport peek retries" }, 454 455 /* system and library failures */ 456 457 { "etm_os_nvlist_pack_fail", FMD_TYPE_UINT64, 458 "nvlist_pack failures" }, 459 { "etm_os_nvlist_unpack_fail", FMD_TYPE_UINT64, 460 "nvlist_unpack failures" }, 461 { "etm_os_nvlist_size_fail", FMD_TYPE_UINT64, 462 "nvlist_size failures" }, 463 { "etm_os_pthread_create_fail", FMD_TYPE_UINT64, 464 "pthread_create failures" }, 465 466 /* transport API failures */ 467 468 { "etm_xport_get_ev_addrv_fail", FMD_TYPE_UINT64, 469 "xport get event addrv API failures" }, 470 { "etm_xport_open_fail", FMD_TYPE_UINT64, 471 "xport open API failures" }, 472 { "etm_xport_close_fail", FMD_TYPE_UINT64, 473 "xport close API failures" }, 474 { "etm_xport_accept_fail", FMD_TYPE_UINT64, 475 "xport accept API failures" }, 476 { "etm_xport_open_retry", FMD_TYPE_UINT64, 477 "xport open API retries" }, 478 479 /* FMD entry point bad arguments */ 480 481 { "etm_fmd_recv_badargs", FMD_TYPE_UINT64, 482 "bad arguments from fmd_recv entry point" }, 483 { "etm_fmd_init_badargs", FMD_TYPE_UINT64, 484 "bad arguments from fmd_init entry point" }, 485 { "etm_fmd_fini_badargs", FMD_TYPE_UINT64, 486 "bad arguments from fmd_fini entry point" }, 487 488 /* Alert logging errors */ 489 490 { "etm_log_err", FMD_TYPE_UINT64, 491 "failed to log message to log(7D)" }, 492 { "etm_msg_err", FMD_TYPE_UINT64, 493 "failed to log message to sysmsg(7D)" } 494 }; 495 496 /* 497 * -------------------------- support functions ------------------------------ 498 */ 499 500 /* 501 * Design_Note: Each failure worth reporting to FMD should be done using 502 * a single call to fmd_hdl_error() as it logs an FMA event 503 * for each call. Also be aware that all the fmd_hdl_*() 504 * format strings currently use platform specific *printf() 505 * routines; so "%p" under Solaris does not prepend "0x" to 506 * the outputted hex digits, while Linux and VxWorks do. 507 */ 508 509 /* 510 * etm_show_time - display the current time of day (for debugging) using 511 * the given FMD module handle and annotation string 512 */ 513 514 static void 515 etm_show_time(fmd_hdl_t *hdl, char *note_str) 516 { 517 struct timeval tmv; /* timeval */ 518 519 (void) gettimeofday(&tmv, NULL); 520 fmd_hdl_debug(hdl, "info: %s: cur Unix Epoch time %d.%06d\n", 521 note_str, tmv.tv_sec, tmv.tv_usec); 522 523 } /* etm_show_time() */ 524 525 /* 526 * etm_hexdump - hexdump the given buffer (for debugging) using 527 * the given FMD module handle 528 */ 529 530 static void 531 etm_hexdump(fmd_hdl_t *hdl, void *buf, size_t byte_cnt) 532 { 533 uint8_t *bp; /* byte ptr */ 534 int i, j; /* index */ 535 char cb[80]; /* char buf */ 536 unsigned int n; /* a byte of data for sprintf() */ 537 538 bp = buf; 539 j = 0; 540 541 /* 542 * Design_Note: fmd_hdl_debug() auto adds a newline if missing; 543 * hence cb exists to accumulate a longer string. 544 */ 545 546 for (i = 1; i <= byte_cnt; i++) { 547 n = *bp++; 548 (void) sprintf(&cb[j], "%2.2x ", n); 549 j += 3; 550 /* add a newline every 16 bytes or at the buffer's end */ 551 if (((i % 16) == 0) || (i >= byte_cnt)) { 552 cb[j-1] = '\0'; 553 fmd_hdl_debug(hdl, "%s\n", cb); 554 j = 0; 555 } 556 } /* for each byte in the buffer */ 557 558 } /* etm_hexdump() */ 559 560 /* 561 * etm_sleep - sleep the caller for the given number of seconds, 562 * return 0 or -errno value 563 * 564 * Design_Note: To avoid interfering with FMD's signal mask (SIGALRM) 565 * do not use [Solaris] sleep(3C) and instead use 566 * pthread_cond_wait() or nanosleep(), both of which 567 * are POSIX spec-ed to leave signal masks alone. 568 * This is needed for Solaris and Linux (domain and SP). 569 */ 570 571 static int 572 etm_sleep(unsigned sleep_sec) 573 { 574 struct timespec tms; /* for nanosleep() */ 575 576 tms.tv_sec = sleep_sec; 577 tms.tv_nsec = 0; 578 579 if (nanosleep(&tms, NULL) < 0) { 580 /* errno assumed set by above call */ 581 return (-errno); 582 } 583 return (0); 584 585 } /* etm_sleep() */ 586 587 /* 588 * etm_conn_open - open a connection to the given transport address, 589 * return 0 and the opened connection handle 590 * or -errno value 591 * 592 * caveats: the err_substr is used in failure cases for calling 593 * fmd_hdl_error() 594 */ 595 596 static int 597 etm_conn_open(fmd_hdl_t *hdl, char *err_substr, 598 etm_xport_addr_t addr, etm_xport_conn_t *connp) 599 { 600 etm_xport_conn_t conn; /* connection to return */ 601 int nev; /* -errno value */ 602 603 if ((conn = etm_xport_open(hdl, addr)) == NULL) { 604 nev = (-errno); 605 fmd_hdl_error(hdl, "error: %s: errno %d\n", 606 err_substr, errno); 607 etm_stats.etm_xport_open_fail.fmds_value.ui64++; 608 return (nev); 609 } else { 610 *connp = conn; 611 return (0); 612 } 613 } /* etm_conn_open() */ 614 615 /* 616 * etm_conn_close - close the given connection, 617 * return 0 or -errno value 618 * 619 * caveats: the err_substr is used in failure cases for calling 620 * fmd_hdl_error() 621 */ 622 623 static int 624 etm_conn_close(fmd_hdl_t *hdl, char *err_substr, etm_xport_conn_t conn) 625 { 626 int nev; /* -errno value */ 627 628 if (etm_xport_close(hdl, conn) == NULL) { 629 nev = (-errno); 630 fmd_hdl_error(hdl, "warning: %s: errno %d\n", 631 err_substr, errno); 632 etm_stats.etm_xport_close_fail.fmds_value.ui64++; 633 return (nev); 634 } else { 635 return (0); 636 } 637 } /* etm_conn_close() */ 638 639 /* 640 * etm_io_op - perform an IO operation on the given connection 641 * with the given buffer, 642 * accommodating MTU size and retrying op if needed, 643 * return how many bytes actually done by the op 644 * or -errno value 645 * 646 * caveats: the err_substr is used in failure cases for calling 647 * fmd_hdl_error() 648 */ 649 650 static ssize_t 651 etm_io_op(fmd_hdl_t *hdl, char *err_substr, etm_xport_conn_t conn, 652 void *buf, size_t byte_cnt, int io_op) 653 { 654 ssize_t rv; /* ret val / byte count */ 655 ssize_t n; /* gen use */ 656 uint8_t *datap; /* ptr to data */ 657 size_t mtu_sz; /* MTU size in bytes */ 658 int (*io_func_ptr)(fmd_hdl_t *, etm_xport_conn_t, 659 void *, size_t); 660 size_t io_sz; /* byte count for io_func_ptr */ 661 int try_cnt; /* number of tries done */ 662 int sleep_sec; /* exp backoff sleep period in sec */ 663 int sleep_rv; /* ret val from sleeping */ 664 fmd_stat_t io_retry_stat; /* IO retry stat to update */ 665 fmd_stat_t io_fail_stat; /* IO failure stat to update */ 666 667 if ((conn == NULL) || (buf == NULL)) { 668 return (-EINVAL); 669 } 670 switch (io_op) { 671 case ETM_IO_OP_RD: 672 io_func_ptr = etm_xport_read; 673 io_retry_stat = etm_stats.etm_xport_rd_retry; 674 io_fail_stat = etm_stats.etm_xport_rd_fail; 675 break; 676 case ETM_IO_OP_WR: 677 io_func_ptr = etm_xport_write; 678 io_retry_stat = etm_stats.etm_xport_wr_retry; 679 io_fail_stat = etm_stats.etm_xport_wr_fail; 680 break; 681 default: 682 return (-EINVAL); 683 } 684 if (byte_cnt == 0) { 685 return (byte_cnt); /* nop */ 686 } 687 688 /* obtain [current] MTU size */ 689 690 if ((n = etm_xport_get_opt(hdl, conn, ETM_XPORT_OPT_MTU_SZ)) < 0) { 691 mtu_sz = ETM_XPORT_MTU_SZ_DEF; 692 } else { 693 mtu_sz = n; 694 } 695 696 /* loop until all IO done, try limit exceeded, or real failure */ 697 698 rv = 0; 699 datap = buf; 700 while (rv < byte_cnt) { 701 io_sz = MIN((byte_cnt - rv), mtu_sz); 702 try_cnt = 0; 703 sleep_sec = 0; 704 705 /* when give up, return -errno value even if partly done */ 706 707 while ((n = (*io_func_ptr)(hdl, conn, datap, io_sz)) == 708 (-EAGAIN)) { 709 try_cnt++; 710 if (try_cnt > ETM_TRY_MAX_CNT) { 711 rv = n; 712 goto func_ret; 713 } 714 if (etm_is_dying) { 715 rv = (-EINTR); 716 goto func_ret; 717 } 718 if ((sleep_rv = etm_sleep(sleep_sec)) < 0) { 719 rv = sleep_rv; 720 goto func_ret; 721 } 722 sleep_sec = ((sleep_sec == 0) ? 1 : 723 (sleep_sec * ETM_TRY_BACKOFF_RATE)); 724 sleep_sec = MIN(sleep_sec, ETM_TRY_BACKOFF_CAP); 725 io_retry_stat.fmds_value.ui64++; 726 if (etm_debug_lvl >= 1) { 727 fmd_hdl_debug(hdl, "info: retrying io op %d " 728 "due to EAGAIN\n", io_op); 729 } 730 } /* while trying the io operation */ 731 732 if (etm_is_dying) { 733 rv = (-EINTR); 734 goto func_ret; 735 } 736 if (n < 0) { 737 rv = n; 738 goto func_ret; 739 } 740 /* avoid spinning CPU when given 0 bytes but no error */ 741 if (n == 0) { 742 if ((sleep_rv = etm_sleep(ETM_SLEEP_QUIK)) < 0) { 743 rv = sleep_rv; 744 goto func_ret; 745 } 746 } 747 rv += n; 748 datap += n; 749 } /* while still have more data */ 750 751 func_ret: 752 753 if (rv < 0) { 754 io_fail_stat.fmds_value.ui64++; 755 fmd_hdl_debug(hdl, "error: %s: errno %d\n", 756 err_substr, (int)(-rv)); 757 } 758 if (etm_debug_lvl >= 3) { 759 fmd_hdl_debug(hdl, "info: io op %d ret %d of %d\n", 760 io_op, (int)rv, (int)byte_cnt); 761 } 762 return (rv); 763 764 } /* etm_io_op() */ 765 766 /* 767 * etm_magic_read - read the magic number of an ETM message header 768 * from the given connection into the given buffer, 769 * return 0 or -errno value 770 * 771 * Design_Note: This routine is intended to help protect ETM from protocol 772 * framing errors as might be caused by an SP reset / crash in 773 * the middle of an ETM message send; the connection will be 774 * read from for as many bytes as needed until the magic number 775 * is found using a sliding buffer for comparisons. 776 */ 777 778 static int 779 etm_magic_read(fmd_hdl_t *hdl, etm_xport_conn_t conn, uint32_t *magic_ptr) 780 { 781 int rv; /* ret val */ 782 uint32_t magic_num; /* magic number */ 783 int byte_cnt; /* count of bytes read */ 784 uint8_t buf5[4+1]; /* sliding input buffer */ 785 int i, j; /* indices into buf5 */ 786 ssize_t n; /* gen use */ 787 uint8_t drop_buf[1024]; /* dropped bytes buffer */ 788 789 rv = 0; /* assume success */ 790 magic_num = 0; 791 byte_cnt = 0; 792 j = 0; 793 794 /* magic number bytes are sent in network (big endian) order */ 795 796 while (magic_num != ETM_PROTO_MAGIC_NUM) { 797 if ((n = etm_io_op(hdl, "bad io read on magic", 798 conn, &buf5[j], 1, ETM_IO_OP_RD)) < 0) { 799 rv = n; 800 goto func_ret; 801 } 802 byte_cnt++; 803 j = MIN((j + 1), sizeof (magic_num)); 804 if (byte_cnt < sizeof (magic_num)) { 805 continue; 806 } 807 808 if (byte_cnt > sizeof (magic_num)) { 809 etm_stats.etm_magic_drop_bytes.fmds_value.ui64++; 810 i = MIN(byte_cnt - j - 1, sizeof (drop_buf) - 1); 811 drop_buf[i] = buf5[0]; 812 for (i = 0; i < j; i++) { 813 buf5[i] = buf5[i+1]; 814 } /* for sliding the buffer contents */ 815 } 816 (void) memcpy(&magic_num, &buf5[0], sizeof (magic_num)); 817 magic_num = ntohl(magic_num); 818 } /* for reading bytes until find magic number */ 819 820 func_ret: 821 822 if (byte_cnt != sizeof (magic_num)) { 823 fmd_hdl_debug(hdl, "warning: bad proto frame " 824 "implies corrupt/lost msg(s)\n"); 825 } 826 if ((byte_cnt > sizeof (magic_num)) && (etm_debug_lvl >= 2)) { 827 i = MIN(byte_cnt - sizeof (magic_num), sizeof (drop_buf)); 828 fmd_hdl_debug(hdl, "info: magic drop hexdump " 829 "first %d of %d bytes:\n", i, 830 byte_cnt - sizeof (magic_num)); 831 etm_hexdump(hdl, drop_buf, i); 832 } 833 834 if (rv == 0) { 835 *magic_ptr = magic_num; 836 } 837 return (rv); 838 839 } /* etm_magic_read() */ 840 841 /* 842 * etm_hdr_read - allocate, read, and validate a [variable sized] 843 * ETM message header from the given connection, 844 * return the allocated ETM message header 845 * (which is guaranteed to be large enough to reuse as a 846 * RESPONSE msg hdr) and its size 847 * or NULL and set errno on failure 848 */ 849 850 static void * 851 etm_hdr_read(fmd_hdl_t *hdl, etm_xport_conn_t conn, size_t *szp) 852 { 853 uint8_t *hdrp; /* ptr to header to return */ 854 size_t hdr_sz; /* sizeof *hdrp */ 855 etm_proto_v1_pp_t pp; /* protocol preamble */ 856 etm_proto_v1_ev_hdr_t *ev_hdrp; /* for FMA_EVENT msg */ 857 etm_proto_v1_ctl_hdr_t *ctl_hdrp; /* for CONTROL msg */ 858 etm_proto_v1_resp_hdr_t *resp_hdrp; /* for RESPONSE msg */ 859 etm_proto_v3_sa_hdr_t *sa_hdrp; /* for ALERT msg */ 860 uint32_t *lenp; /* ptr to FMA event length */ 861 ssize_t i, n; /* gen use */ 862 uint8_t misc_buf[ETM_MISC_BUF_SZ]; /* for var sized hdrs */ 863 int dummy_int; /* dummy var to appease lint */ 864 865 hdrp = NULL; hdr_sz = 0; 866 867 /* read the magic number which starts the protocol preamble */ 868 869 if ((n = etm_magic_read(hdl, conn, &pp.pp_magic_num)) < 0) { 870 errno = (-n); 871 etm_stats.etm_magic_bad.fmds_value.ui64++; 872 return (NULL); 873 } 874 875 /* read the rest of the protocol preamble all at once */ 876 877 if ((n = etm_io_op(hdl, "bad io read on preamble", 878 conn, &pp.pp_proto_ver, sizeof (pp) - sizeof (pp.pp_magic_num), 879 ETM_IO_OP_RD)) < 0) { 880 errno = (-n); 881 return (NULL); 882 } 883 884 /* 885 * Design_Note: The magic number was already network decoded; but 886 * some other preamble fields also need to be decoded, 887 * specifically pp_xid and pp_timeout. The rest of the 888 * preamble fields are byte sized and hence need no 889 * decoding. 890 */ 891 892 pp.pp_xid = ntohl(pp.pp_xid); 893 pp.pp_timeout = ntohl(pp.pp_timeout); 894 895 /* sanity check the header as best we can */ 896 897 if ((pp.pp_proto_ver < ETM_PROTO_V1) || 898 (pp.pp_proto_ver > ETM_PROTO_V3)) { 899 fmd_hdl_error(hdl, "error: bad proto ver %d\n", 900 (int)pp.pp_proto_ver); 901 errno = EPROTO; 902 etm_stats.etm_ver_bad.fmds_value.ui64++; 903 return (NULL); 904 } 905 906 dummy_int = pp.pp_msg_type; 907 if ((dummy_int <= ETM_MSG_TYPE_TOO_LOW) || 908 (dummy_int >= ETM_MSG_TYPE_TOO_BIG)) { 909 fmd_hdl_error(hdl, "error: bad msg type %d", dummy_int); 910 errno = EBADMSG; 911 etm_stats.etm_msgtype_bad.fmds_value.ui64++; 912 return (NULL); 913 } 914 915 /* handle [var sized] hdrs for FMA_EVENT, CONTROL, RESPONSE msgs */ 916 917 if (pp.pp_msg_type == ETM_MSG_TYPE_FMA_EVENT) { 918 919 ev_hdrp = (void*)&misc_buf[0]; 920 hdr_sz = sizeof (*ev_hdrp); 921 (void) memcpy(&ev_hdrp->ev_pp, &pp, sizeof (pp)); 922 923 /* sanity check the header's timeout */ 924 925 if ((ev_hdrp->ev_pp.pp_proto_ver == ETM_PROTO_V1) && 926 (ev_hdrp->ev_pp.pp_timeout != ETM_PROTO_V1_TIMEOUT_NONE)) { 927 errno = ETIME; 928 etm_stats.etm_timeout_bad.fmds_value.ui64++; 929 return (NULL); 930 } 931 932 /* get all FMA event lengths from the header */ 933 934 lenp = (uint32_t *)&ev_hdrp->ev_lens[0]; lenp--; 935 i = -1; /* cnt of length entries preceding 0 */ 936 do { 937 i++; lenp++; 938 if ((sizeof (*ev_hdrp) + (i * sizeof (*lenp))) >= 939 ETM_MISC_BUF_SZ) { 940 errno = E2BIG; /* ridiculous size */ 941 etm_stats.etm_evlens_bad.fmds_value.ui64++; 942 return (NULL); 943 } 944 if ((n = etm_io_op(hdl, "bad io read on event len", 945 conn, lenp, sizeof (*lenp), ETM_IO_OP_RD)) < 0) { 946 errno = (-n); 947 return (NULL); 948 } 949 *lenp = ntohl(*lenp); 950 951 } while (*lenp != 0); 952 i += 0; /* first len already counted by sizeof(ev_hdr) */ 953 hdr_sz += (i * sizeof (*lenp)); 954 955 etm_stats.etm_rd_hdr_fmaevent.fmds_value.ui64++; 956 957 } else if (pp.pp_msg_type == ETM_MSG_TYPE_CONTROL) { 958 959 ctl_hdrp = (void*)&misc_buf[0]; 960 hdr_sz = sizeof (*ctl_hdrp); 961 (void) memcpy(&ctl_hdrp->ctl_pp, &pp, sizeof (pp)); 962 963 /* sanity check the header's sub type (control selector) */ 964 965 if ((ctl_hdrp->ctl_pp.pp_sub_type <= ETM_CTL_SEL_TOO_LOW) || 966 (ctl_hdrp->ctl_pp.pp_sub_type >= ETM_CTL_SEL_TOO_BIG)) { 967 fmd_hdl_error(hdl, "error: bad ctl sub type %d\n", 968 (int)ctl_hdrp->ctl_pp.pp_sub_type); 969 errno = EBADMSG; 970 etm_stats.etm_subtype_bad.fmds_value.ui64++; 971 return (NULL); 972 } 973 974 /* get the control length */ 975 976 if ((n = etm_io_op(hdl, "bad io read on ctl len", 977 conn, &ctl_hdrp->ctl_len, sizeof (ctl_hdrp->ctl_len), 978 ETM_IO_OP_RD)) < 0) { 979 errno = (-n); 980 return (NULL); 981 } 982 983 ctl_hdrp->ctl_len = ntohl(ctl_hdrp->ctl_len); 984 985 etm_stats.etm_rd_hdr_control.fmds_value.ui64++; 986 987 } else if (pp.pp_msg_type == ETM_MSG_TYPE_RESPONSE) { 988 989 resp_hdrp = (void*)&misc_buf[0]; 990 hdr_sz = sizeof (*resp_hdrp); 991 (void) memcpy(&resp_hdrp->resp_pp, &pp, sizeof (pp)); 992 993 /* sanity check the header's timeout */ 994 995 if (resp_hdrp->resp_pp.pp_timeout != 996 ETM_PROTO_V1_TIMEOUT_NONE) { 997 errno = ETIME; 998 etm_stats.etm_timeout_bad.fmds_value.ui64++; 999 return (NULL); 1000 } 1001 1002 /* get the response code and length */ 1003 1004 if ((n = etm_io_op(hdl, "bad io read on resp code+len", 1005 conn, &resp_hdrp->resp_code, 1006 sizeof (resp_hdrp->resp_code) 1007 + sizeof (resp_hdrp->resp_len), 1008 ETM_IO_OP_RD)) < 0) { 1009 errno = (-n); 1010 return (NULL); 1011 } 1012 1013 resp_hdrp->resp_code = ntohl(resp_hdrp->resp_code); 1014 resp_hdrp->resp_len = ntohl(resp_hdrp->resp_len); 1015 1016 etm_stats.etm_rd_hdr_response.fmds_value.ui64++; 1017 1018 } else if (pp.pp_msg_type == ETM_MSG_TYPE_ALERT) { 1019 1020 sa_hdrp = (void*)&misc_buf[0]; 1021 hdr_sz = sizeof (*sa_hdrp); 1022 (void) memcpy(&sa_hdrp->sa_pp, &pp, sizeof (pp)); 1023 1024 /* sanity check the header's protocol version */ 1025 1026 if (sa_hdrp->sa_pp.pp_proto_ver != ETM_PROTO_V3) { 1027 errno = EPROTO; 1028 etm_stats.etm_ver_bad.fmds_value.ui64++; 1029 return (NULL); 1030 } 1031 1032 /* get the priority and length */ 1033 1034 if ((n = etm_io_op(hdl, "bad io read on sa priority+len", 1035 conn, &sa_hdrp->sa_priority, 1036 sizeof (sa_hdrp->sa_priority) 1037 + sizeof (sa_hdrp->sa_len), 1038 ETM_IO_OP_RD)) < 0) { 1039 errno = (-n); 1040 return (NULL); 1041 } 1042 1043 sa_hdrp->sa_priority = ntohl(sa_hdrp->sa_priority); 1044 sa_hdrp->sa_len = ntohl(sa_hdrp->sa_len); 1045 1046 etm_stats.etm_rd_hdr_alert.fmds_value.ui64++; 1047 1048 } /* whether we have FMA_EVENT, ALERT, CONTROL, or RESPONSE msg */ 1049 1050 /* 1051 * choose a header size that allows hdr reuse for RESPONSE msgs, 1052 * allocate and populate the message header, and 1053 * return alloc size to caller for later free of hdrp 1054 */ 1055 1056 hdr_sz = MAX(hdr_sz, sizeof (*resp_hdrp)); 1057 hdrp = fmd_hdl_zalloc(hdl, hdr_sz, FMD_SLEEP); 1058 (void) memcpy(hdrp, misc_buf, hdr_sz); 1059 1060 if (etm_debug_lvl >= 3) { 1061 fmd_hdl_debug(hdl, "info: msg hdr hexdump %d bytes:\n", hdr_sz); 1062 etm_hexdump(hdl, hdrp, hdr_sz); 1063 } 1064 *szp = hdr_sz; 1065 return (hdrp); 1066 1067 } /* etm_hdr_read() */ 1068 1069 /* 1070 * etm_hdr_write - create and write a [variable sized] ETM message header 1071 * to the given connection appropriate for the given FMA event 1072 * and type of nvlist encoding, 1073 * return the allocated ETM message header and its size 1074 * or NULL and set errno on failure 1075 */ 1076 1077 static void* 1078 etm_hdr_write(fmd_hdl_t *hdl, etm_xport_conn_t conn, nvlist_t *evp, 1079 int encoding, size_t *szp) 1080 { 1081 etm_proto_v1_ev_hdr_t *hdrp; /* for FMA_EVENT msg */ 1082 size_t hdr_sz; /* sizeof *hdrp */ 1083 uint32_t *lenp; /* ptr to FMA event length */ 1084 size_t evsz; /* packed FMA event size */ 1085 ssize_t n; /* gen use */ 1086 1087 /* allocate and populate the message header for 1 FMA event */ 1088 1089 hdr_sz = sizeof (*hdrp) + (1 * sizeof (hdrp->ev_lens[0])); 1090 1091 hdrp = fmd_hdl_zalloc(hdl, hdr_sz, FMD_SLEEP); 1092 1093 /* 1094 * Design_Note: Although the ETM protocol supports it, we do not (yet) 1095 * want responses/ACKs on FMA events that we send. All 1096 * such messages are sent with ETM_PROTO_V1_TIMEOUT_NONE. 1097 */ 1098 1099 hdrp->ev_pp.pp_magic_num = ETM_PROTO_MAGIC_NUM; 1100 hdrp->ev_pp.pp_magic_num = htonl(hdrp->ev_pp.pp_magic_num); 1101 hdrp->ev_pp.pp_proto_ver = ETM_PROTO_V1; 1102 hdrp->ev_pp.pp_msg_type = ETM_MSG_TYPE_FMA_EVENT; 1103 hdrp->ev_pp.pp_sub_type = 0; 1104 hdrp->ev_pp.pp_rsvd_pad = 0; 1105 hdrp->ev_pp.pp_xid = etm_xid_cur; 1106 hdrp->ev_pp.pp_xid = htonl(hdrp->ev_pp.pp_xid); 1107 etm_xid_cur += ETM_XID_INC; 1108 hdrp->ev_pp.pp_timeout = ETM_PROTO_V1_TIMEOUT_NONE; 1109 hdrp->ev_pp.pp_timeout = htonl(hdrp->ev_pp.pp_timeout); 1110 1111 lenp = &hdrp->ev_lens[0]; 1112 1113 if ((n = nvlist_size(evp, &evsz, encoding)) != 0) { 1114 errno = n; 1115 fmd_hdl_free(hdl, hdrp, hdr_sz); 1116 etm_stats.etm_os_nvlist_size_fail.fmds_value.ui64++; 1117 return (NULL); 1118 } 1119 1120 /* indicate 1 FMA event, network encode its length, and 0-terminate */ 1121 1122 etm_stats.etm_wr_max_ev_per_msg.fmds_value.ui64 = 1; 1123 1124 *lenp = evsz; *lenp = htonl(*lenp); lenp++; 1125 *lenp = 0; *lenp = htonl(*lenp); lenp++; 1126 1127 /* 1128 * write the network encoded header to the transport, and 1129 * return alloc size to caller for later free 1130 */ 1131 1132 if ((n = etm_io_op(hdl, "bad io write on event hdr", 1133 conn, hdrp, hdr_sz, ETM_IO_OP_WR)) < 0) { 1134 errno = (-n); 1135 fmd_hdl_free(hdl, hdrp, hdr_sz); 1136 return (NULL); 1137 } 1138 1139 *szp = hdr_sz; 1140 return (hdrp); 1141 1142 } /* etm_hdr_write() */ 1143 1144 /* 1145 * etm_post_to_fmd - post the given FMA event to FMD 1146 * via a FMD transport API call, 1147 * return 0 or -errno value 1148 * 1149 * caveats: the FMA event (evp) is freed by FMD, 1150 * thus callers of this function should 1151 * immediately discard any ptr they have to the 1152 * nvlist without freeing or dereferencing it 1153 */ 1154 1155 static int 1156 etm_post_to_fmd(fmd_hdl_t *hdl, nvlist_t *evp) 1157 { 1158 ssize_t ev_sz; /* sizeof *evp */ 1159 1160 (void) nvlist_size(evp, (size_t *)&ev_sz, NV_ENCODE_XDR); 1161 1162 if (etm_debug_lvl >= 2) { 1163 etm_show_time(hdl, "ante ev post"); 1164 } 1165 fmd_xprt_post(hdl, etm_fmd_xprt, evp, 0); 1166 etm_stats.etm_wr_fmd_fmaevent.fmds_value.ui64++; 1167 etm_stats.etm_wr_fmd_bytes.fmds_value.ui64 += ev_sz; 1168 if (etm_debug_lvl >= 1) { 1169 fmd_hdl_debug(hdl, "info: event %p post ok to FMD\n", evp); 1170 } 1171 if (etm_debug_lvl >= 2) { 1172 etm_show_time(hdl, "post ev post"); 1173 } 1174 return (0); 1175 1176 } /* etm_post_to_fmd() */ 1177 1178 /* 1179 * Ideally we would just use syslog(3C) for outputting our messages. 1180 * Unfortunately, as this module is running within the FMA daemon context, 1181 * that would create the situation where this module's openlog() would 1182 * have the monopoly on syslog(3C) for the daemon and all its modules. 1183 * To avoid that situation, this module uses the same logic as the 1184 * syslog-msgs FM module to directly call into the log(7D) and sysmsg(7D) 1185 * devices for syslog and console. 1186 */ 1187 1188 static int 1189 etm_post_to_syslog(fmd_hdl_t *hdl, uint32_t priority, uint32_t body_sz, 1190 uint8_t *body_buf) 1191 { 1192 char *sysmessage; /* Formatted message */ 1193 size_t formatlen; /* maximum length of sysmessage */ 1194 struct strbuf ctl, dat; /* structs pushed to the logfd */ 1195 uint32_t msgid; /* syslog message ID number */ 1196 1197 if ((syslog_file == 0) && (syslog_cons == 0)) { 1198 return (0); 1199 } 1200 1201 if (etm_debug_lvl >= 2) { 1202 etm_show_time(hdl, "ante syslog post"); 1203 } 1204 1205 formatlen = body_sz + 64; /* +64 for prefix strings added below */ 1206 sysmessage = fmd_hdl_zalloc(hdl, formatlen, FMD_SLEEP); 1207 1208 if (syslog_file) { 1209 STRLOG_MAKE_MSGID(body_buf, msgid); 1210 (void) snprintf(sysmessage, formatlen, 1211 "SC Alert: [ID %u FACILITY_AND_PRIORITY] %s", msgid, 1212 body_buf); 1213 1214 syslog_ctl.pri = syslog_facility | priority; 1215 1216 ctl.buf = (void *)&syslog_ctl; 1217 ctl.len = sizeof (syslog_ctl); 1218 1219 dat.buf = sysmessage; 1220 dat.len = strlen(sysmessage) + 1; 1221 1222 if (putmsg(syslog_logfd, &ctl, &dat, 0) != 0) { 1223 fmd_hdl_debug(hdl, "putmsg failed: %s\n", 1224 strerror(errno)); 1225 etm_stats.etm_log_err.fmds_value.ui64++; 1226 } 1227 } 1228 1229 if (syslog_cons) { 1230 (void) snprintf(sysmessage, formatlen, 1231 "SC Alert: %s\r\n", body_buf); 1232 1233 dat.buf = sysmessage; 1234 dat.len = strlen(sysmessage) + 1; 1235 1236 if (write(syslog_msgfd, dat.buf, dat.len) != dat.len) { 1237 fmd_hdl_debug(hdl, "write failed: %s\n", 1238 strerror(errno)); 1239 etm_stats.etm_msg_err.fmds_value.ui64++; 1240 } 1241 } 1242 1243 fmd_hdl_free(hdl, sysmessage, formatlen); 1244 1245 if (etm_debug_lvl >= 2) { 1246 etm_show_time(hdl, "post syslog post"); 1247 } 1248 1249 return (0); 1250 } 1251 1252 1253 /* 1254 * etm_req_ver_negot - send an ETM control message to the other end requesting 1255 * that the ETM protocol version be negotiated/set 1256 */ 1257 1258 static void 1259 etm_req_ver_negot(fmd_hdl_t *hdl) 1260 { 1261 etm_xport_addr_t *addrv; /* default dst addr(s) */ 1262 etm_xport_conn_t conn; /* connection to other end */ 1263 etm_proto_v1_ctl_hdr_t *ctl_hdrp; /* for CONTROL msg */ 1264 size_t hdr_sz; /* sizeof header */ 1265 uint8_t *body_buf; /* msg body buffer */ 1266 uint32_t body_sz; /* sizeof *body_buf */ 1267 ssize_t i; /* gen use */ 1268 1269 /* populate an ETM control msg to send */ 1270 1271 hdr_sz = sizeof (*ctl_hdrp); 1272 body_sz = (3 + 1); /* version bytes plus null byte */ 1273 1274 ctl_hdrp = fmd_hdl_zalloc(hdl, hdr_sz + body_sz, FMD_SLEEP); 1275 1276 ctl_hdrp->ctl_pp.pp_magic_num = htonl(ETM_PROTO_MAGIC_NUM); 1277 ctl_hdrp->ctl_pp.pp_proto_ver = ETM_PROTO_V1; 1278 ctl_hdrp->ctl_pp.pp_msg_type = ETM_MSG_TYPE_CONTROL; 1279 ctl_hdrp->ctl_pp.pp_sub_type = ETM_CTL_SEL_VER_NEGOT_REQ; 1280 ctl_hdrp->ctl_pp.pp_rsvd_pad = 0; 1281 etm_xid_ver_negot = etm_xid_cur; 1282 etm_xid_cur += ETM_XID_INC; 1283 ctl_hdrp->ctl_pp.pp_xid = htonl(etm_xid_ver_negot); 1284 ctl_hdrp->ctl_pp.pp_timeout = htonl(ETM_PROTO_V1_TIMEOUT_FOREVER); 1285 ctl_hdrp->ctl_len = htonl(body_sz); 1286 1287 body_buf = (void*)&ctl_hdrp->ctl_len; 1288 body_buf += sizeof (ctl_hdrp->ctl_len); 1289 *body_buf++ = ETM_PROTO_V3; 1290 *body_buf++ = ETM_PROTO_V2; 1291 *body_buf++ = ETM_PROTO_V1; 1292 *body_buf++ = '\0'; 1293 1294 /* 1295 * open and close a connection to send the ETM control msg 1296 * to any/all of the default dst addrs 1297 */ 1298 1299 if ((addrv = etm_xport_get_ev_addrv(hdl, NULL)) == NULL) { 1300 fmd_hdl_error(hdl, 1301 "error: bad ctl dst addrs errno %d\n", errno); 1302 etm_stats.etm_xport_get_ev_addrv_fail.fmds_value.ui64++; 1303 goto func_ret; 1304 } 1305 1306 for (i = 0; addrv[i] != NULL; i++) { 1307 1308 if (etm_conn_open(hdl, "bad conn open during ver negot", 1309 addrv[i], &conn) < 0) { 1310 continue; 1311 } 1312 if (etm_io_op(hdl, "bad io write on ctl hdr+body", 1313 conn, ctl_hdrp, hdr_sz + body_sz, ETM_IO_OP_WR) >= 0) { 1314 etm_stats.etm_wr_hdr_control.fmds_value.ui64++; 1315 etm_stats.etm_wr_body_control.fmds_value.ui64++; 1316 } 1317 (void) etm_conn_close(hdl, "bad conn close during ver negot", 1318 conn); 1319 1320 } /* foreach dst addr */ 1321 1322 func_ret: 1323 1324 if (addrv != NULL) { 1325 etm_xport_free_addrv(hdl, addrv); 1326 } 1327 fmd_hdl_free(hdl, ctl_hdrp, hdr_sz + body_sz); 1328 1329 } /* etm_req_ver_negot() */ 1330 1331 /* 1332 * Design_Note: For all etm_resp_q_*() functions and etm_resp_q_* globals, 1333 * the mutex etm_resp_q_lock must be held by the caller. 1334 */ 1335 1336 /* 1337 * etm_resp_q_enq - add element to tail of ETM responder queue 1338 * etm_resp_q_deq - del element from head of ETM responder queue 1339 * 1340 * return >0 for success, or -errno value 1341 */ 1342 1343 static int 1344 etm_resp_q_enq(fmd_hdl_t *hdl, etm_resp_q_ele_t *rqep) 1345 { 1346 etm_resp_q_ele_t *newp; /* ptr to new resp q ele */ 1347 1348 if (etm_resp_q_cur_len >= etm_resp_q_max_len) { 1349 fmd_hdl_debug(hdl, "warning: enq to full responder queue\n"); 1350 etm_stats.etm_enq_drop_resp_q.fmds_value.ui64++; 1351 return (-E2BIG); 1352 } 1353 1354 newp = fmd_hdl_zalloc(hdl, sizeof (*newp), FMD_SLEEP); 1355 (void) memcpy(newp, rqep, sizeof (*newp)); 1356 newp->rqe_nextp = NULL; 1357 1358 if (etm_resp_q_cur_len == 0) { 1359 etm_resp_q_head = newp; 1360 } else { 1361 etm_resp_q_tail->rqe_nextp = newp; 1362 } 1363 etm_resp_q_tail = newp; 1364 etm_resp_q_cur_len++; 1365 etm_stats.etm_resp_q_cur_len.fmds_value.ui64 = etm_resp_q_cur_len; 1366 1367 return (1); 1368 1369 } /* etm_resp_q_enq() */ 1370 1371 static int 1372 etm_resp_q_deq(fmd_hdl_t *hdl, etm_resp_q_ele_t *rqep) 1373 { 1374 etm_resp_q_ele_t *oldp; /* ptr to old resp q ele */ 1375 1376 if (etm_resp_q_cur_len == 0) { 1377 fmd_hdl_debug(hdl, "warning: deq from empty responder queue\n"); 1378 etm_stats.etm_deq_drop_resp_q.fmds_value.ui64++; 1379 return (-ENOENT); 1380 } 1381 1382 (void) memcpy(rqep, etm_resp_q_head, sizeof (*rqep)); 1383 rqep->rqe_nextp = NULL; 1384 1385 oldp = etm_resp_q_head; 1386 etm_resp_q_head = etm_resp_q_head->rqe_nextp; 1387 fmd_hdl_free(hdl, oldp, sizeof (*oldp)); 1388 1389 etm_resp_q_cur_len--; 1390 etm_stats.etm_resp_q_cur_len.fmds_value.ui64 = etm_resp_q_cur_len; 1391 if (etm_resp_q_cur_len == 0) { 1392 etm_resp_q_tail = NULL; 1393 } 1394 1395 return (1); 1396 1397 } /* etm_resp_q_deq() */ 1398 1399 /* 1400 * etm_maybe_enq_response - check the given message header to see 1401 * whether a response has been requested, 1402 * if so then enqueue the given connection 1403 * and header for later transport by the 1404 * responder thread as an ETM response msg, 1405 * return 0 for nop, >0 success, or -errno value 1406 */ 1407 1408 static ssize_t 1409 etm_maybe_enq_response(fmd_hdl_t *hdl, etm_xport_conn_t conn, 1410 void *hdrp, uint32_t hdr_sz, int32_t resp_code) 1411 { 1412 ssize_t rv; /* ret val */ 1413 etm_proto_v1_pp_t *ppp; /* protocol preamble ptr */ 1414 uint8_t orig_msg_type; /* orig hdr's message type */ 1415 uint32_t orig_timeout; /* orig hdr's timeout */ 1416 etm_resp_q_ele_t rqe; /* responder queue ele */ 1417 1418 ppp = hdrp; 1419 orig_msg_type = ppp->pp_msg_type; 1420 orig_timeout = ppp->pp_timeout; 1421 1422 /* bail out now if no response is to be sent */ 1423 1424 if (orig_timeout == ETM_PROTO_V1_TIMEOUT_NONE) { 1425 return (0); 1426 } /* if a nop */ 1427 1428 if ((orig_msg_type != ETM_MSG_TYPE_FMA_EVENT) && 1429 (orig_msg_type != ETM_MSG_TYPE_ALERT) && 1430 (orig_msg_type != ETM_MSG_TYPE_CONTROL)) { 1431 fmd_hdl_debug(hdl, "warning: bad msg type 0x%x\n", 1432 orig_msg_type); 1433 return (-EINVAL); 1434 } /* if inappropriate hdr for a response msg */ 1435 1436 /* 1437 * enqueue the msg hdr and nudge the responder thread 1438 * if the responder queue was previously empty 1439 */ 1440 1441 rqe.rqe_conn = conn; 1442 rqe.rqe_hdrp = hdrp; 1443 rqe.rqe_hdr_sz = hdr_sz; 1444 rqe.rqe_resp_code = resp_code; 1445 1446 (void) pthread_mutex_lock(&etm_resp_q_lock); 1447 rv = etm_resp_q_enq(hdl, &rqe); 1448 if (etm_resp_q_cur_len == 1) 1449 (void) pthread_cond_signal(&etm_resp_q_cv); 1450 (void) pthread_mutex_unlock(&etm_resp_q_lock); 1451 1452 return (rv); 1453 1454 } /* etm_maybe_enq_response() */ 1455 1456 /* 1457 * Design_Note: We rely on the fact that all message types have 1458 * a common protocol preamble; if this fact should 1459 * ever change it may break the code below. We also 1460 * rely on the fact that FMA_EVENT and CONTROL headers 1461 * returned by etm_hdr_read() will be sized large enough 1462 * to reuse them as RESPONSE headers if the remote endpt 1463 * asked for a response via the pp_timeout field. 1464 */ 1465 1466 /* 1467 * etm_send_response - use the given message header and response code 1468 * to construct an appropriate response message, 1469 * and send it back on the given connection, 1470 * return >0 for success, or -errno value 1471 */ 1472 1473 static ssize_t 1474 etm_send_response(fmd_hdl_t *hdl, etm_xport_conn_t conn, 1475 void *hdrp, int32_t resp_code) 1476 { 1477 ssize_t rv; /* ret val */ 1478 etm_proto_v1_pp_t *ppp; /* protocol preamble ptr */ 1479 etm_proto_v1_resp_hdr_t *resp_hdrp; /* for RESPONSE msg */ 1480 uint8_t resp_body[4]; /* response body if needed */ 1481 uint8_t *resp_msg; /* response hdr+body */ 1482 size_t hdr_sz; /* sizeof response hdr */ 1483 uint8_t orig_msg_type; /* orig hdr's message type */ 1484 1485 ppp = hdrp; 1486 orig_msg_type = ppp->pp_msg_type; 1487 1488 if (etm_debug_lvl >= 2) { 1489 etm_show_time(hdl, "ante resp send"); 1490 } 1491 1492 /* reuse the given header as a response header */ 1493 1494 resp_hdrp = hdrp; 1495 resp_hdrp->resp_code = resp_code; 1496 resp_hdrp->resp_len = 0; /* default is empty body */ 1497 1498 if ((orig_msg_type == ETM_MSG_TYPE_CONTROL) && 1499 (ppp->pp_sub_type == ETM_CTL_SEL_VER_NEGOT_REQ)) { 1500 resp_body[0] = ETM_PROTO_V2; 1501 resp_body[1] = ETM_PROTO_V3; 1502 resp_body[2] = 0; 1503 resp_hdrp->resp_len = 3; 1504 } /* if should send our/negotiated proto ver in resp body */ 1505 1506 /* respond with the proto ver that was negotiated */ 1507 1508 resp_hdrp->resp_pp.pp_proto_ver = etm_resp_ver; 1509 resp_hdrp->resp_pp.pp_msg_type = ETM_MSG_TYPE_RESPONSE; 1510 resp_hdrp->resp_pp.pp_timeout = ETM_PROTO_V1_TIMEOUT_NONE; 1511 1512 /* 1513 * send the whole response msg in one write, header and body; 1514 * avoid the alloc-and-copy if we can reuse the hdr as the msg, 1515 * ie, if the body is empty. update the response stats. 1516 */ 1517 1518 hdr_sz = sizeof (etm_proto_v1_resp_hdr_t); 1519 1520 resp_msg = hdrp; 1521 if (resp_hdrp->resp_len > 0) { 1522 resp_msg = fmd_hdl_zalloc(hdl, hdr_sz + resp_hdrp->resp_len, 1523 FMD_SLEEP); 1524 (void) memcpy(resp_msg, resp_hdrp, hdr_sz); 1525 (void) memcpy(resp_msg + hdr_sz, resp_body, 1526 resp_hdrp->resp_len); 1527 } 1528 1529 (void) pthread_mutex_lock(&etm_write_lock); 1530 rv = etm_io_op(hdl, "bad io write on resp msg", conn, 1531 resp_msg, hdr_sz + resp_hdrp->resp_len, ETM_IO_OP_WR); 1532 (void) pthread_mutex_unlock(&etm_write_lock); 1533 if (rv < 0) { 1534 goto func_ret; 1535 } 1536 1537 etm_stats.etm_wr_hdr_response.fmds_value.ui64++; 1538 etm_stats.etm_wr_body_response.fmds_value.ui64++; 1539 1540 fmd_hdl_debug(hdl, "info: sent V%u RESPONSE msg to xport " 1541 "xid 0x%x code %d len %u\n", 1542 (unsigned int)resp_hdrp->resp_pp.pp_proto_ver, 1543 resp_hdrp->resp_pp.pp_xid, resp_hdrp->resp_code, 1544 resp_hdrp->resp_len); 1545 func_ret: 1546 1547 if (resp_hdrp->resp_len > 0) { 1548 fmd_hdl_free(hdl, resp_msg, hdr_sz + resp_hdrp->resp_len); 1549 } 1550 if (etm_debug_lvl >= 2) { 1551 etm_show_time(hdl, "post resp send"); 1552 } 1553 return (rv); 1554 1555 } /* etm_send_response() */ 1556 1557 /* 1558 * etm_handle_new_conn - receive an ETM message sent from the other end via 1559 * the given open connection, pull out any FMA events 1560 * and post them to the local FMD (or handle any ETM 1561 * control or response msg); when done, close the 1562 * connection 1563 */ 1564 1565 static void 1566 etm_handle_new_conn(fmd_hdl_t *hdl, etm_xport_conn_t conn) 1567 { 1568 etm_proto_v1_ev_hdr_t *ev_hdrp; /* for FMA_EVENT msg */ 1569 etm_proto_v1_ctl_hdr_t *ctl_hdrp; /* for CONTROL msg */ 1570 etm_proto_v1_resp_hdr_t *resp_hdrp; /* for RESPONSE msg */ 1571 etm_proto_v3_sa_hdr_t *sa_hdrp; /* for ALERT msg */ 1572 int32_t resp_code; /* response code */ 1573 ssize_t enq_rv; /* resp_q enqueue status */ 1574 size_t hdr_sz; /* sizeof header */ 1575 uint8_t *body_buf; /* msg body buffer */ 1576 uint32_t body_sz; /* sizeof body_buf */ 1577 uint32_t ev_cnt; /* count of FMA events */ 1578 uint8_t *bp; /* byte ptr within body_buf */ 1579 nvlist_t *evp; /* ptr to unpacked FMA event */ 1580 char *class; /* FMA event class */ 1581 ssize_t i, n; /* gen use */ 1582 1583 if (etm_debug_lvl >= 2) { 1584 etm_show_time(hdl, "ante conn handle"); 1585 } 1586 fmd_hdl_debug(hdl, "info: handling new conn %p\n", conn); 1587 1588 ev_hdrp = NULL; 1589 ctl_hdrp = NULL; 1590 resp_hdrp = NULL; 1591 sa_hdrp = NULL; 1592 body_buf = NULL; 1593 class = NULL; 1594 evp = NULL; 1595 resp_code = 0; /* default is success */ 1596 enq_rv = 0; /* default is nop, ie, did not enqueue */ 1597 1598 /* read a network decoded message header from the connection */ 1599 1600 if ((ev_hdrp = etm_hdr_read(hdl, conn, &hdr_sz)) == NULL) { 1601 /* errno assumed set by above call */ 1602 fmd_hdl_debug(hdl, "error: FMA event dropped: " 1603 "bad hdr read errno %d\n", errno); 1604 etm_stats.etm_rd_drop_fmaevent.fmds_value.ui64++; 1605 goto func_ret; 1606 } 1607 1608 /* 1609 * handle the message based on its preamble pp_msg_type 1610 * which is known to be valid from etm_hdr_read() checks 1611 */ 1612 1613 if (ev_hdrp->ev_pp.pp_msg_type == ETM_MSG_TYPE_FMA_EVENT) { 1614 1615 fmd_hdl_debug(hdl, "info: rcvd FMA_EVENT msg from xport\n"); 1616 1617 /* allocate buf large enough for whole body / all FMA events */ 1618 1619 body_sz = 0; 1620 for (i = 0; ev_hdrp->ev_lens[i] != 0; i++) { 1621 body_sz += ev_hdrp->ev_lens[i]; 1622 } /* for summing sizes of all FMA events */ 1623 if (i > etm_stats.etm_rd_max_ev_per_msg.fmds_value.ui64) 1624 etm_stats.etm_rd_max_ev_per_msg.fmds_value.ui64 = i; 1625 ev_cnt = i; 1626 1627 if (etm_debug_lvl >= 1) { 1628 fmd_hdl_debug(hdl, "info: event lengths %u sum %u\n", 1629 ev_cnt, body_sz); 1630 } 1631 1632 body_buf = fmd_hdl_zalloc(hdl, body_sz, FMD_SLEEP); 1633 1634 /* read all the FMA events at once */ 1635 1636 if ((n = etm_io_op(hdl, "FMA event dropped: " 1637 "bad io read on event bodies", conn, body_buf, body_sz, 1638 ETM_IO_OP_RD)) < 0) { 1639 etm_stats.etm_rd_drop_fmaevent.fmds_value.ui64++; 1640 goto func_ret; 1641 } 1642 1643 etm_stats.etm_rd_xport_bytes.fmds_value.ui64 += body_sz; 1644 etm_stats.etm_rd_body_fmaevent.fmds_value.ui64 += ev_cnt; 1645 1646 /* 1647 * now that we've read the entire ETM msg from the conn, 1648 * which avoids later ETM protocol framing errors if we didn't, 1649 * check for dup msg/xid against last good FMD posting, 1650 * if a dup then resend response but skip repost to FMD 1651 */ 1652 1653 if (ev_hdrp->ev_pp.pp_xid == etm_xid_posted_ev) { 1654 enq_rv = etm_maybe_enq_response(hdl, conn, 1655 ev_hdrp, hdr_sz, 0); 1656 fmd_hdl_debug(hdl, "info: skipping dup FMA event post " 1657 "xid 0x%x\n", etm_xid_posted_ev); 1658 etm_stats.etm_rd_dup_fmaevent.fmds_value.ui64++; 1659 goto func_ret; 1660 } 1661 1662 /* unpack each FMA event and post it to FMD */ 1663 1664 bp = body_buf; 1665 for (i = 0; i < ev_cnt; i++) { 1666 if ((n = nvlist_unpack((char *)bp, 1667 ev_hdrp->ev_lens[i], &evp, 0)) != 0) { 1668 resp_code = (-n); 1669 enq_rv = etm_maybe_enq_response(hdl, conn, 1670 ev_hdrp, hdr_sz, resp_code); 1671 fmd_hdl_error(hdl, "error: FMA event dropped: " 1672 "bad event body unpack errno %d\n", n); 1673 if (etm_debug_lvl >= 2) { 1674 fmd_hdl_debug(hdl, "info: FMA event " 1675 "hexdump %d bytes:\n", 1676 ev_hdrp->ev_lens[i]); 1677 etm_hexdump(hdl, bp, 1678 ev_hdrp->ev_lens[i]); 1679 } 1680 etm_stats.etm_os_nvlist_unpack_fail.fmds_value. 1681 ui64++; 1682 etm_stats.etm_rd_drop_fmaevent.fmds_value. 1683 ui64++; 1684 bp += ev_hdrp->ev_lens[i]; 1685 continue; 1686 } 1687 if (etm_debug_lvl >= 1) { 1688 (void) nvlist_lookup_string(evp, FM_CLASS, 1689 &class); 1690 if (class == NULL) { 1691 class = "NULL"; 1692 } 1693 fmd_hdl_debug(hdl, "info: FMA event %p " 1694 "class %s\n", evp, class); 1695 } 1696 resp_code = etm_post_to_fmd(hdl, evp); 1697 if (resp_code >= 0) { 1698 etm_xid_posted_ev = ev_hdrp->ev_pp.pp_xid; 1699 } 1700 evp = NULL; 1701 enq_rv = etm_maybe_enq_response(hdl, conn, 1702 ev_hdrp, hdr_sz, resp_code); 1703 bp += ev_hdrp->ev_lens[i]; 1704 } /* foreach FMA event in the body buffer */ 1705 1706 } else if (ev_hdrp->ev_pp.pp_msg_type == ETM_MSG_TYPE_CONTROL) { 1707 1708 ctl_hdrp = (void*)ev_hdrp; 1709 1710 fmd_hdl_debug(hdl, "info: rcvd CONTROL msg from xport\n"); 1711 if (etm_debug_lvl >= 1) { 1712 fmd_hdl_debug(hdl, "info: ctl sel %d xid 0x%x\n", 1713 (int)ctl_hdrp->ctl_pp.pp_sub_type, 1714 ctl_hdrp->ctl_pp.pp_xid); 1715 } 1716 1717 /* 1718 * if we have a VER_NEGOT_REQ read the body and validate 1719 * the protocol version set contained therein, 1720 * otherwise we have a PING_REQ (which has no body) 1721 * and we [also] fall thru to the code which sends a 1722 * response msg if the pp_timeout field requested one 1723 */ 1724 1725 if (ctl_hdrp->ctl_pp.pp_sub_type == ETM_CTL_SEL_VER_NEGOT_REQ) { 1726 1727 body_sz = ctl_hdrp->ctl_len; 1728 body_buf = fmd_hdl_zalloc(hdl, body_sz, FMD_SLEEP); 1729 1730 if ((n = etm_io_op(hdl, "bad io read on ctl body", 1731 conn, body_buf, body_sz, ETM_IO_OP_RD)) < 0) { 1732 goto func_ret; 1733 } 1734 1735 /* complain if version set completely incompatible */ 1736 1737 for (i = 0; i < body_sz; i++) { 1738 if ((body_buf[i] == ETM_PROTO_V1) || 1739 (body_buf[i] == ETM_PROTO_V2) || 1740 (body_buf[i] == ETM_PROTO_V3)) { 1741 break; 1742 } 1743 } 1744 if (i >= body_sz) { 1745 etm_stats.etm_ver_bad.fmds_value.ui64++; 1746 resp_code = (-EPROTO); 1747 } 1748 1749 } /* if got version set request */ 1750 1751 etm_stats.etm_rd_body_control.fmds_value.ui64++; 1752 1753 enq_rv = etm_maybe_enq_response(hdl, conn, 1754 ctl_hdrp, hdr_sz, resp_code); 1755 1756 } else if (ev_hdrp->ev_pp.pp_msg_type == ETM_MSG_TYPE_RESPONSE) { 1757 1758 resp_hdrp = (void*)ev_hdrp; 1759 1760 fmd_hdl_debug(hdl, "info: rcvd RESPONSE msg from xport\n"); 1761 if (etm_debug_lvl >= 1) { 1762 fmd_hdl_debug(hdl, "info: resp xid 0x%x\n", 1763 (int)resp_hdrp->resp_pp.pp_xid); 1764 } 1765 1766 body_sz = resp_hdrp->resp_len; 1767 body_buf = fmd_hdl_zalloc(hdl, body_sz, FMD_SLEEP); 1768 1769 if ((n = etm_io_op(hdl, "bad io read on resp len", 1770 conn, body_buf, body_sz, ETM_IO_OP_RD)) < 0) { 1771 goto func_ret; 1772 } 1773 1774 etm_stats.etm_rd_body_response.fmds_value.ui64++; 1775 1776 /* 1777 * look up the xid to interpret the response body 1778 * 1779 * ping is a nop; for ver negot confirm that a supported 1780 * protocol version was negotiated and remember which one 1781 */ 1782 1783 if ((resp_hdrp->resp_pp.pp_xid != etm_xid_ping) && 1784 (resp_hdrp->resp_pp.pp_xid != etm_xid_ver_negot)) { 1785 etm_stats.etm_xid_bad.fmds_value.ui64++; 1786 goto func_ret; 1787 } 1788 1789 if (resp_hdrp->resp_pp.pp_xid == etm_xid_ver_negot) { 1790 if ((body_buf[0] < ETM_PROTO_V1) || 1791 (body_buf[0] > ETM_PROTO_V3)) { 1792 etm_stats.etm_ver_bad.fmds_value.ui64++; 1793 goto func_ret; 1794 } 1795 etm_resp_ver = body_buf[0]; 1796 } /* if have resp to last req to negotiate proto ver */ 1797 1798 } else if (ev_hdrp->ev_pp.pp_msg_type == ETM_MSG_TYPE_ALERT) { 1799 1800 sa_hdrp = (void*)ev_hdrp; 1801 1802 fmd_hdl_debug(hdl, "info: rcvd ALERT msg from xport\n"); 1803 if (etm_debug_lvl >= 1) { 1804 fmd_hdl_debug(hdl, "info: sa sel %d xid 0x%x\n", 1805 (int)sa_hdrp->sa_pp.pp_sub_type, 1806 sa_hdrp->sa_pp.pp_xid); 1807 } 1808 1809 body_sz = sa_hdrp->sa_len; 1810 body_buf = fmd_hdl_zalloc(hdl, body_sz, FMD_SLEEP); 1811 1812 if ((n = etm_io_op(hdl, "bad io read on sa body", 1813 conn, body_buf, body_sz, ETM_IO_OP_RD)) < 0) { 1814 goto func_ret; 1815 } 1816 1817 etm_stats.etm_rd_body_alert.fmds_value.ui64++; 1818 1819 /* 1820 * now that we've read the entire ETM msg from the conn, 1821 * which avoids later ETM protocol framing errors if we didn't, 1822 * check for dup msg/xid against last good syslog posting, 1823 * if a dup then resend response but skip repost to syslog 1824 */ 1825 1826 if (sa_hdrp->sa_pp.pp_xid == etm_xid_posted_sa) { 1827 enq_rv = etm_maybe_enq_response(hdl, conn, 1828 sa_hdrp, hdr_sz, 0); 1829 fmd_hdl_debug(hdl, "info: skipping dup ALERT post " 1830 "xid 0x%x\n", etm_xid_posted_sa); 1831 etm_stats.etm_rd_dup_alert.fmds_value.ui64++; 1832 goto func_ret; 1833 } 1834 1835 resp_code = etm_post_to_syslog(hdl, sa_hdrp->sa_priority, 1836 body_sz, body_buf); 1837 if (resp_code >= 0) { 1838 etm_xid_posted_sa = sa_hdrp->sa_pp.pp_xid; 1839 } 1840 enq_rv = etm_maybe_enq_response(hdl, conn, 1841 sa_hdrp, hdr_sz, resp_code); 1842 } /* whether we have a FMA_EVENT, CONTROL, RESPONSE or ALERT msg */ 1843 1844 func_ret: 1845 1846 if (etm_debug_lvl >= 2) { 1847 etm_show_time(hdl, "post conn handle"); 1848 } 1849 1850 /* 1851 * if no responder ele was enqueued, close the conn now 1852 * and free the ETM msg hdr; the ETM msg body is not needed 1853 * by the responder thread and should always be freed here 1854 */ 1855 1856 if (enq_rv <= 0) { 1857 (void) etm_conn_close(hdl, "bad conn close after msg recv", 1858 conn); 1859 if (ev_hdrp != NULL) { 1860 fmd_hdl_free(hdl, ev_hdrp, hdr_sz); 1861 } 1862 } 1863 if (body_buf != NULL) { 1864 fmd_hdl_free(hdl, body_buf, body_sz); 1865 } 1866 } /* etm_handle_new_conn() */ 1867 1868 /* 1869 * etm_server - loop forever accepting new connections 1870 * using the given FMD handle, 1871 * handling any ETM msgs sent from the other side 1872 * via each such connection 1873 */ 1874 1875 static void 1876 etm_server(void *arg) 1877 { 1878 etm_xport_conn_t conn; /* connection handle */ 1879 ssize_t n; /* gen use */ 1880 fmd_hdl_t *hdl; /* FMD handle */ 1881 1882 hdl = arg; 1883 1884 fmd_hdl_debug(hdl, "info: connection server starting\n"); 1885 1886 while (!etm_is_dying) { 1887 1888 if ((conn = etm_xport_accept(hdl, NULL)) == NULL) { 1889 /* errno assumed set by above call */ 1890 n = errno; 1891 if (etm_is_dying) { 1892 break; 1893 } 1894 fmd_hdl_debug(hdl, 1895 "error: bad conn accept errno %d\n", n); 1896 etm_stats.etm_xport_accept_fail.fmds_value.ui64++; 1897 /* avoid spinning CPU */ 1898 (void) etm_sleep(ETM_SLEEP_SLOW); 1899 continue; 1900 } 1901 1902 /* handle the new message/connection, closing it when done */ 1903 1904 etm_handle_new_conn(hdl, conn); 1905 1906 } /* while accepting new connections until ETM dies */ 1907 1908 /* ETM is dying (probably due to "fmadm unload etm") */ 1909 1910 fmd_hdl_debug(hdl, "info: connection server is dying\n"); 1911 1912 } /* etm_server() */ 1913 1914 /* 1915 * etm_responder - loop forever waiting for new responder queue elements 1916 * to be enqueued, for each one constructing and sending 1917 * an ETM response msg to the other side, and closing its 1918 * associated connection when appropriate 1919 * 1920 * this thread exists to ensure that the etm_server() thread 1921 * never pends indefinitely waiting on the xport write lock, and is 1922 * hence always available to accept new connections and handle 1923 * incoming messages 1924 * 1925 * this design relies on the fact that each connection accepted and 1926 * returned by the ETM xport layer is unique, and each can be closed 1927 * independently of the others while multiple connections are 1928 * outstanding 1929 */ 1930 1931 static void 1932 etm_responder(void *arg) 1933 { 1934 ssize_t n; /* gen use */ 1935 fmd_hdl_t *hdl; /* FMD handle */ 1936 etm_resp_q_ele_t rqe; /* responder queue ele */ 1937 1938 hdl = arg; 1939 1940 fmd_hdl_debug(hdl, "info: responder server starting\n"); 1941 1942 while (!etm_is_dying) { 1943 1944 (void) pthread_mutex_lock(&etm_resp_q_lock); 1945 1946 while (etm_resp_q_cur_len == 0) { 1947 (void) pthread_cond_wait(&etm_resp_q_cv, 1948 &etm_resp_q_lock); 1949 if (etm_is_dying) { 1950 (void) pthread_mutex_unlock(&etm_resp_q_lock); 1951 goto func_ret; 1952 } 1953 } /* while the responder queue is empty, wait to be nudged */ 1954 1955 /* 1956 * for every responder ele that has been enqueued, 1957 * dequeue and send it as an ETM response msg, 1958 * closing its associated conn and freeing its hdr 1959 * 1960 * enter the queue draining loop holding the responder 1961 * queue lock, but do not hold the lock indefinitely 1962 * (the actual send may pend us indefinitely), 1963 * so that other threads will never pend for long 1964 * trying to enqueue a new element 1965 */ 1966 1967 while (etm_resp_q_cur_len > 0) { 1968 1969 (void) etm_resp_q_deq(hdl, &rqe); 1970 (void) pthread_mutex_unlock(&etm_resp_q_lock); 1971 1972 if ((n = etm_send_response(hdl, rqe.rqe_conn, 1973 rqe.rqe_hdrp, rqe.rqe_resp_code)) < 0) { 1974 fmd_hdl_error(hdl, "error: bad resp send " 1975 "errno %d\n", (-n)); 1976 } 1977 1978 (void) etm_conn_close(hdl, "bad conn close after resp", 1979 rqe.rqe_conn); 1980 fmd_hdl_free(hdl, rqe.rqe_hdrp, rqe.rqe_hdr_sz); 1981 1982 if (etm_is_dying) { 1983 goto func_ret; 1984 } 1985 (void) pthread_mutex_lock(&etm_resp_q_lock); 1986 1987 } /* while draining the responder queue */ 1988 1989 (void) pthread_mutex_unlock(&etm_resp_q_lock); 1990 1991 } /* while awaiting and sending resp msgs until ETM dies */ 1992 1993 func_ret: 1994 1995 /* ETM is dying (probably due to "fmadm unload etm") */ 1996 1997 fmd_hdl_debug(hdl, "info: responder server is dying\n"); 1998 1999 (void) pthread_mutex_lock(&etm_resp_q_lock); 2000 if (etm_resp_q_cur_len > 0) { 2001 fmd_hdl_error(hdl, "warning: %d response msgs dropped\n", 2002 (int)etm_resp_q_cur_len); 2003 while (etm_resp_q_cur_len > 0) { 2004 (void) etm_resp_q_deq(hdl, &rqe); 2005 (void) etm_conn_close(hdl, "bad conn close after deq", 2006 rqe.rqe_conn); 2007 fmd_hdl_free(hdl, rqe.rqe_hdrp, rqe.rqe_hdr_sz); 2008 } 2009 } 2010 (void) pthread_mutex_unlock(&etm_resp_q_lock); 2011 2012 } /* etm_responder() */ 2013 2014 static void * 2015 etm_init_alloc(size_t size) 2016 { 2017 return (fmd_hdl_alloc(init_hdl, size, FMD_SLEEP)); 2018 } 2019 2020 static void 2021 etm_init_free(void *addr, size_t size) 2022 { 2023 fmd_hdl_free(init_hdl, addr, size); 2024 } 2025 2026 /* 2027 * -------------------------- FMD entry points ------------------------------- 2028 */ 2029 2030 /* 2031 * _fmd_init - initialize the transport for use by ETM and start the 2032 * server daemon to accept new connections to us 2033 * 2034 * FMD will read our *.conf and subscribe us to FMA events 2035 */ 2036 2037 void 2038 _fmd_init(fmd_hdl_t *hdl) 2039 { 2040 struct timeval tmv; /* timeval */ 2041 ssize_t n; /* gen use */ 2042 ldom_hdl_t *lhp; /* ldom pointer */ 2043 const struct facility *fp; /* syslog facility matching */ 2044 char *facname; /* syslog facility property */ 2045 2046 if (fmd_hdl_register(hdl, FMD_API_VERSION, &fmd_info) != 0) { 2047 return; /* invalid data in configuration file */ 2048 } 2049 2050 fmd_hdl_debug(hdl, "info: module initializing\n"); 2051 2052 init_hdl = hdl; 2053 lhp = ldom_init(etm_init_alloc, etm_init_free); 2054 2055 /* 2056 * Do not load this module if it is runing on a guest ldom. 2057 */ 2058 if (ldom_major_version(lhp) == 1 && ldom_on_service(lhp) == 0) { 2059 fmd_hdl_debug(hdl, "info: module unregistering\n"); 2060 ldom_fini(lhp); 2061 fmd_hdl_unregister(hdl); 2062 return; 2063 } else { 2064 ldom_fini(lhp); 2065 } 2066 2067 /* setup statistics and properties from FMD */ 2068 2069 (void) fmd_stat_create(hdl, FMD_STAT_NOALLOC, 2070 sizeof (etm_stats) / sizeof (fmd_stat_t), (fmd_stat_t *)&etm_stats); 2071 2072 etm_debug_lvl = fmd_prop_get_int32(hdl, ETM_PROP_NM_DEBUG_LVL); 2073 etm_debug_max_ev_cnt = fmd_prop_get_int32(hdl, 2074 ETM_PROP_NM_DEBUG_MAX_EV_CNT); 2075 fmd_hdl_debug(hdl, "info: etm_debug_lvl %d " 2076 "etm_debug_max_ev_cnt %d\n", etm_debug_lvl, etm_debug_max_ev_cnt); 2077 2078 etm_resp_q_max_len = fmd_prop_get_int32(hdl, 2079 ETM_PROP_NM_MAX_RESP_Q_LEN); 2080 etm_stats.etm_resp_q_max_len.fmds_value.ui64 = etm_resp_q_max_len; 2081 2082 /* obtain an FMD transport handle so we can post FMA events later */ 2083 2084 etm_fmd_xprt = fmd_xprt_open(hdl, FMD_XPRT_RDONLY, NULL, NULL); 2085 2086 /* encourage protocol transaction id to be unique per module load */ 2087 2088 (void) gettimeofday(&tmv, NULL); 2089 etm_xid_cur = (uint32_t)((tmv.tv_sec << 10) | 2090 ((unsigned long)tmv.tv_usec >> 10)); 2091 2092 /* init the ETM transport */ 2093 2094 if ((n = etm_xport_init(hdl)) != 0) { 2095 fmd_hdl_error(hdl, "error: bad xport init errno %d\n", (-n)); 2096 fmd_hdl_unregister(hdl); 2097 return; 2098 } 2099 2100 /* 2101 * Cache any properties we use every time we receive an alert. 2102 */ 2103 syslog_file = fmd_prop_get_int32(hdl, ETM_PROP_NM_SYSLOGD); 2104 syslog_cons = fmd_prop_get_int32(hdl, ETM_PROP_NM_CONSOLE); 2105 2106 if (syslog_file && (syslog_logfd = open("/dev/conslog", 2107 O_WRONLY | O_NOCTTY)) == -1) { 2108 fmd_hdl_error(hdl, "error: failed to open /dev/conslog"); 2109 syslog_file = 0; 2110 } 2111 2112 if (syslog_cons && (syslog_msgfd = open("/dev/sysmsg", 2113 O_WRONLY | O_NOCTTY)) == -1) { 2114 fmd_hdl_error(hdl, "error: failed to open /dev/sysmsg"); 2115 syslog_cons = 0; 2116 } 2117 2118 if (syslog_file) { 2119 /* 2120 * Look up the value of the "facility" property and use it to 2121 * determine * what syslog LOG_* facility value we use to 2122 * fill in our log_ctl_t. 2123 */ 2124 facname = fmd_prop_get_string(hdl, ETM_PROP_NM_FACILITY); 2125 2126 for (fp = syslog_facs; fp->fac_name != NULL; fp++) { 2127 if (strcmp(fp->fac_name, facname) == 0) 2128 break; 2129 } 2130 2131 if (fp->fac_name == NULL) { 2132 fmd_hdl_error(hdl, "error: invalid 'facility'" 2133 " setting: %s\n", facname); 2134 syslog_file = 0; 2135 } else { 2136 syslog_facility = fp->fac_value; 2137 syslog_ctl.flags = SL_CONSOLE | SL_LOGONLY; 2138 } 2139 2140 fmd_prop_free_string(hdl, facname); 2141 } 2142 2143 /* 2144 * start the message responder and the connection acceptance server; 2145 * request protocol version be negotiated after waiting a second 2146 * for the receiver to be ready to start handshaking 2147 */ 2148 2149 etm_resp_tid = fmd_thr_create(hdl, etm_responder, hdl); 2150 etm_svr_tid = fmd_thr_create(hdl, etm_server, hdl); 2151 2152 (void) etm_sleep(ETM_SLEEP_QUIK); 2153 etm_req_ver_negot(hdl); 2154 2155 fmd_hdl_debug(hdl, "info: module initialized ok\n"); 2156 2157 } /* _fmd_init() */ 2158 2159 /* 2160 * etm_recv - receive an FMA event from FMD and transport it 2161 * to the remote endpoint 2162 */ 2163 2164 /*ARGSUSED*/ 2165 void 2166 etm_recv(fmd_hdl_t *hdl, fmd_event_t *ep, nvlist_t *evp, const char *class) 2167 { 2168 etm_xport_addr_t *addrv; /* vector of transport addresses */ 2169 etm_xport_conn_t conn; /* connection handle */ 2170 etm_proto_v1_ev_hdr_t *hdrp; /* for FMA_EVENT msg */ 2171 ssize_t i, n; /* gen use */ 2172 size_t sz; /* header size */ 2173 size_t buflen; /* size of packed FMA event */ 2174 uint8_t *buf; /* tmp buffer for packed FMA event */ 2175 2176 buflen = 0; 2177 if ((n = nvlist_size(evp, &buflen, NV_ENCODE_XDR)) != 0) { 2178 fmd_hdl_error(hdl, "error: FMA event dropped: " 2179 "event size errno %d class %s\n", n, class); 2180 etm_stats.etm_os_nvlist_size_fail.fmds_value.ui64++; 2181 etm_stats.etm_wr_drop_fmaevent.fmds_value.ui64++; 2182 return; 2183 } 2184 2185 fmd_hdl_debug(hdl, "info: rcvd event %p from FMD\n", evp); 2186 fmd_hdl_debug(hdl, "info: cnt %llu class %s\n", 2187 etm_stats.etm_rd_fmd_fmaevent.fmds_value.ui64, class); 2188 2189 etm_stats.etm_rd_fmd_bytes.fmds_value.ui64 += buflen; 2190 etm_stats.etm_rd_fmd_fmaevent.fmds_value.ui64++; 2191 2192 /* 2193 * if the debug limit has been set, avoid excessive traffic, 2194 * for example, an infinite cycle using loopback nodes 2195 */ 2196 2197 if ((etm_debug_max_ev_cnt >= 0) && 2198 (etm_stats.etm_rd_fmd_fmaevent.fmds_value.ui64 > 2199 etm_debug_max_ev_cnt)) { 2200 fmd_hdl_debug(hdl, "warning: FMA event dropped: " 2201 "event %p cnt %llu > debug max %d\n", evp, 2202 etm_stats.etm_rd_fmd_fmaevent.fmds_value.ui64, 2203 etm_debug_max_ev_cnt); 2204 etm_stats.etm_wr_drop_fmaevent.fmds_value.ui64++; 2205 return; 2206 } 2207 2208 /* allocate a buffer for the FMA event and nvlist pack it */ 2209 2210 buf = fmd_hdl_zalloc(hdl, buflen, FMD_SLEEP); 2211 2212 if ((n = nvlist_pack(evp, (char **)&buf, &buflen, 2213 NV_ENCODE_XDR, 0)) != 0) { 2214 fmd_hdl_error(hdl, "error: FMA event dropped: " 2215 "event pack errno %d class %s\n", n, class); 2216 etm_stats.etm_os_nvlist_pack_fail.fmds_value.ui64++; 2217 etm_stats.etm_wr_drop_fmaevent.fmds_value.ui64++; 2218 fmd_hdl_free(hdl, buf, buflen); 2219 return; 2220 } 2221 2222 /* get vector of dst addrs and send the FMA event to each one */ 2223 2224 if ((addrv = etm_xport_get_ev_addrv(hdl, evp)) == NULL) { 2225 fmd_hdl_error(hdl, "error: FMA event dropped: " 2226 "bad event dst addrs errno %d\n", errno); 2227 etm_stats.etm_xport_get_ev_addrv_fail.fmds_value.ui64++; 2228 etm_stats.etm_wr_drop_fmaevent.fmds_value.ui64++; 2229 fmd_hdl_free(hdl, buf, buflen); 2230 return; 2231 } 2232 2233 for (i = 0; addrv[i] != NULL; i++) { 2234 2235 /* open a new connection to this dst addr */ 2236 2237 if ((n = etm_conn_open(hdl, "FMA event dropped: " 2238 "bad conn open on new ev", addrv[i], &conn)) < 0) { 2239 etm_stats.etm_wr_drop_fmaevent.fmds_value.ui64++; 2240 continue; 2241 } 2242 2243 (void) pthread_mutex_lock(&etm_write_lock); 2244 2245 /* write the ETM message header */ 2246 2247 if ((hdrp = etm_hdr_write(hdl, conn, evp, NV_ENCODE_XDR, 2248 &sz)) == NULL) { 2249 (void) pthread_mutex_unlock(&etm_write_lock); 2250 fmd_hdl_error(hdl, "error: FMA event dropped: " 2251 "bad hdr write errno %d\n", errno); 2252 (void) etm_conn_close(hdl, 2253 "bad conn close per bad hdr wr", conn); 2254 etm_stats.etm_wr_drop_fmaevent.fmds_value.ui64++; 2255 continue; 2256 } 2257 2258 fmd_hdl_free(hdl, hdrp, sz); /* header not needed */ 2259 etm_stats.etm_wr_hdr_fmaevent.fmds_value.ui64++; 2260 fmd_hdl_debug(hdl, "info: hdr xport write ok for event %p\n", 2261 evp); 2262 2263 /* write the ETM message body, ie, the packed nvlist */ 2264 2265 if ((n = etm_io_op(hdl, "FMA event dropped: " 2266 "bad io write on event", conn, 2267 buf, buflen, ETM_IO_OP_WR)) < 0) { 2268 (void) pthread_mutex_unlock(&etm_write_lock); 2269 (void) etm_conn_close(hdl, 2270 "bad conn close per bad body wr", conn); 2271 etm_stats.etm_wr_drop_fmaevent.fmds_value.ui64++; 2272 continue; 2273 } 2274 2275 (void) pthread_mutex_unlock(&etm_write_lock); 2276 2277 etm_stats.etm_wr_body_fmaevent.fmds_value.ui64++; 2278 etm_stats.etm_wr_xport_bytes.fmds_value.ui64 += buflen; 2279 fmd_hdl_debug(hdl, "info: body xport write ok for event %p\n", 2280 evp); 2281 2282 /* close the connection */ 2283 2284 (void) etm_conn_close(hdl, "bad conn close after event send", 2285 conn); 2286 } /* foreach dst addr in the vector */ 2287 2288 etm_xport_free_addrv(hdl, addrv); 2289 fmd_hdl_free(hdl, buf, buflen); 2290 2291 } /* etm_recv() */ 2292 2293 /* 2294 * _fmd_fini - stop the server daemon and teardown the transport 2295 */ 2296 2297 void 2298 _fmd_fini(fmd_hdl_t *hdl) 2299 { 2300 ssize_t n; /* gen use */ 2301 2302 fmd_hdl_debug(hdl, "info: module finalizing\n"); 2303 2304 /* kill the connection server and responder ; wait for them to die */ 2305 2306 etm_is_dying = 1; 2307 2308 if (etm_svr_tid != NULL) { 2309 fmd_thr_signal(hdl, etm_svr_tid); 2310 fmd_thr_destroy(hdl, etm_svr_tid); 2311 etm_svr_tid = NULL; 2312 } /* if server thread was successfully created */ 2313 2314 if (etm_resp_tid != NULL) { 2315 fmd_thr_signal(hdl, etm_resp_tid); 2316 fmd_thr_destroy(hdl, etm_resp_tid); 2317 etm_resp_tid = NULL; 2318 } /* if responder thread was successfully created */ 2319 2320 /* teardown the transport and cleanup syslogging */ 2321 2322 if ((n = etm_xport_fini(hdl)) != 0) { 2323 fmd_hdl_error(hdl, "warning: xport fini errno %d\n", (-n)); 2324 } 2325 if (etm_fmd_xprt != NULL) { 2326 fmd_xprt_close(hdl, etm_fmd_xprt); 2327 } 2328 2329 if (syslog_logfd != -1) { 2330 (void) close(syslog_logfd); 2331 } 2332 if (syslog_msgfd != -1) { 2333 (void) close(syslog_msgfd); 2334 } 2335 2336 fmd_hdl_debug(hdl, "info: module finalized ok\n"); 2337 2338 } /* _fmd_fini() */ 2339