17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate * CDDL HEADER START
37c478bd9Sstevel@tonic-gate *
47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the
5*9dd0f810Scindi * Common Development and Distribution License (the "License").
6*9dd0f810Scindi * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate *
87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate * and limitations under the License.
127c478bd9Sstevel@tonic-gate *
137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate *
197c478bd9Sstevel@tonic-gate * CDDL HEADER END
207c478bd9Sstevel@tonic-gate */
21d9638e54Smws
227c478bd9Sstevel@tonic-gate /*
23*9dd0f810Scindi * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
247c478bd9Sstevel@tonic-gate * Use is subject to license terms.
257c478bd9Sstevel@tonic-gate */
267c478bd9Sstevel@tonic-gate
277c478bd9Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI"
287c478bd9Sstevel@tonic-gate
297c478bd9Sstevel@tonic-gate /*
307c478bd9Sstevel@tonic-gate * FMD Log File Subsystem
317c478bd9Sstevel@tonic-gate *
327c478bd9Sstevel@tonic-gate * Events are written to one of two log files as they are received or created;
337c478bd9Sstevel@tonic-gate * the error log tracks all ereport.* events received on the inbound event
347c478bd9Sstevel@tonic-gate * transport, and the fault log tracks all list.* events generated by fmd or
357c478bd9Sstevel@tonic-gate * its client modules. In addition, we use the same log file format to cache
367c478bd9Sstevel@tonic-gate * state and events associated with ASRUs that are named in a diagnosis.
377c478bd9Sstevel@tonic-gate *
387c478bd9Sstevel@tonic-gate * The log files use the exacct format manipulated by libexacct(3LIB) and
397c478bd9Sstevel@tonic-gate * originally defined in PSARC 1999/119. However, the exacct library was
407c478bd9Sstevel@tonic-gate * designed primarily for read-only clients and without the synchronous i/o
417c478bd9Sstevel@tonic-gate * considerations and seeking required for fmd, so we use libexacct here only
427c478bd9Sstevel@tonic-gate * to read and write the file headers and to pack data from memory into a file
437c478bd9Sstevel@tonic-gate * bytestream. All of the i/o and file offset manipulations are performed by
447c478bd9Sstevel@tonic-gate * the fmd code below. Our exacct file management uses the following grammar:
457c478bd9Sstevel@tonic-gate *
467c478bd9Sstevel@tonic-gate * file := hdr toc event*
477ee93e3bSdilpreet * hdr := EXD_FMA_LABEL EXD_FMA_VERSION EXD_FMA_OSREL EXD_FMA_OSVER
487ee93e3bSdilpreet * EXD_FMA_PLAT EXD_FMA_UUID
497c478bd9Sstevel@tonic-gate * toc := EXD_FMA_OFFSET
507ee93e3bSdilpreet * event := EXD_FMA_TODSEC EXD_FMA_TODNSEC EXD_FMA_NVLIST evref* or legacy evref
517ee93e3bSdilpreet * evref := EXD_FMA_UUID EXD_FMA_OFFSET
527ee93e3bSdilpreet * legacy evref := EXD_FMA_MAJOR EXD_FMA_MINOR EXD_FMA_INODE EXD_FMA_OFFSET
537c478bd9Sstevel@tonic-gate *
547c478bd9Sstevel@tonic-gate * Any event can be uniquely identified by the tuple (file, offset) where file
557ee93e3bSdilpreet * is encoded as (uuid) when we are cross-linking files. For legacy file
567ee93e3bSdilpreet * formats we still support encoding the reference as (major, minor, inode).
577ee93e3bSdilpreet * Note that we break out of the file's dev_t into its two 32-bit components to
587c478bd9Sstevel@tonic-gate * permit development of either 32-bit or 64-bit log readers and writers; the
597c478bd9Sstevel@tonic-gate * LFS APIs do not yet export a 64-bit dev_t to fstat64(), so there is no way
607c478bd9Sstevel@tonic-gate * for a 32-bit application to retrieve and store a 64-bit dev_t.
617c478bd9Sstevel@tonic-gate *
627c478bd9Sstevel@tonic-gate * In order to replay events in the event of an fmd crash, events are initially
637c478bd9Sstevel@tonic-gate * written to the error log using the group catalog tag EXD_GROUP_RFMA by the
647c478bd9Sstevel@tonic-gate * fmd_log_append() function. Later, once an event transitions from the
657c478bd9Sstevel@tonic-gate * received state to one of its other states (see fmd_event.c for details),
667c478bd9Sstevel@tonic-gate * fmd_log_commit() is used to overwrite the tag with EXD_GROUP_FMA, indicating
677c478bd9Sstevel@tonic-gate * that the event is fully processed and no longer needs to be replayed.
687c478bd9Sstevel@tonic-gate */
697c478bd9Sstevel@tonic-gate
707c478bd9Sstevel@tonic-gate #include <sys/types.h>
717c478bd9Sstevel@tonic-gate #include <sys/mkdev.h>
727c478bd9Sstevel@tonic-gate #include <sys/statvfs.h>
737c478bd9Sstevel@tonic-gate #include <sys/fm/protocol.h>
747c478bd9Sstevel@tonic-gate #include <sys/exacct_impl.h>
757ee93e3bSdilpreet #include <uuid/uuid.h>
767c478bd9Sstevel@tonic-gate
777c478bd9Sstevel@tonic-gate #include <unistd.h>
787c478bd9Sstevel@tonic-gate #include <limits.h>
797c478bd9Sstevel@tonic-gate #include <fcntl.h>
807c478bd9Sstevel@tonic-gate #include <ctype.h>
817c478bd9Sstevel@tonic-gate
827c478bd9Sstevel@tonic-gate #include <fmd_alloc.h>
837c478bd9Sstevel@tonic-gate #include <fmd_error.h>
847c478bd9Sstevel@tonic-gate #include <fmd_string.h>
857c478bd9Sstevel@tonic-gate #include <fmd_event.h>
867c478bd9Sstevel@tonic-gate #include <fmd_conf.h>
877c478bd9Sstevel@tonic-gate #include <fmd_subr.h>
887c478bd9Sstevel@tonic-gate #include <fmd_case.h>
897c478bd9Sstevel@tonic-gate #include <fmd_log.h>
907c478bd9Sstevel@tonic-gate
917c478bd9Sstevel@tonic-gate #include <fmd.h>
927c478bd9Sstevel@tonic-gate
937c478bd9Sstevel@tonic-gate #define CAT_FMA_RGROUP (EXT_GROUP | EXC_DEFAULT | EXD_GROUP_RFMA)
947c478bd9Sstevel@tonic-gate #define CAT_FMA_GROUP (EXT_GROUP | EXC_DEFAULT | EXD_GROUP_FMA)
957c478bd9Sstevel@tonic-gate
967c478bd9Sstevel@tonic-gate #define CAT_FMA_LABEL (EXT_STRING | EXC_DEFAULT | EXD_FMA_LABEL)
977c478bd9Sstevel@tonic-gate #define CAT_FMA_VERSION (EXT_STRING | EXC_DEFAULT | EXD_FMA_VERSION)
987c478bd9Sstevel@tonic-gate #define CAT_FMA_OSREL (EXT_STRING | EXC_DEFAULT | EXD_FMA_OSREL)
997c478bd9Sstevel@tonic-gate #define CAT_FMA_OSVER (EXT_STRING | EXC_DEFAULT | EXD_FMA_OSVER)
1007c478bd9Sstevel@tonic-gate #define CAT_FMA_PLAT (EXT_STRING | EXC_DEFAULT | EXD_FMA_PLAT)
1017ee93e3bSdilpreet #define CAT_FMA_UUID (EXT_STRING | EXC_DEFAULT | EXD_FMA_UUID)
1027c478bd9Sstevel@tonic-gate #define CAT_FMA_TODSEC (EXT_UINT64 | EXC_DEFAULT | EXD_FMA_TODSEC)
1037c478bd9Sstevel@tonic-gate #define CAT_FMA_TODNSEC (EXT_UINT64 | EXC_DEFAULT | EXD_FMA_TODNSEC)
1047c478bd9Sstevel@tonic-gate #define CAT_FMA_NVLIST (EXT_RAW | EXC_DEFAULT | EXD_FMA_NVLIST)
1057c478bd9Sstevel@tonic-gate #define CAT_FMA_MAJOR (EXT_UINT32 | EXC_DEFAULT | EXD_FMA_MAJOR)
1067c478bd9Sstevel@tonic-gate #define CAT_FMA_MINOR (EXT_UINT32 | EXC_DEFAULT | EXD_FMA_MINOR)
1077c478bd9Sstevel@tonic-gate #define CAT_FMA_INODE (EXT_UINT64 | EXC_DEFAULT | EXD_FMA_INODE)
1087c478bd9Sstevel@tonic-gate #define CAT_FMA_OFFSET (EXT_UINT64 | EXC_DEFAULT | EXD_FMA_OFFSET)
1097c478bd9Sstevel@tonic-gate
1107c478bd9Sstevel@tonic-gate static ssize_t
fmd_log_write(fmd_log_t * lp,const void * buf,size_t n)1117c478bd9Sstevel@tonic-gate fmd_log_write(fmd_log_t *lp, const void *buf, size_t n)
1127c478bd9Sstevel@tonic-gate {
1137c478bd9Sstevel@tonic-gate ssize_t resid = n;
1147c478bd9Sstevel@tonic-gate ssize_t len;
1157c478bd9Sstevel@tonic-gate
1167c478bd9Sstevel@tonic-gate ASSERT(MUTEX_HELD(&lp->log_lock));
1177c478bd9Sstevel@tonic-gate
1187c478bd9Sstevel@tonic-gate while (resid != 0) {
1197c478bd9Sstevel@tonic-gate if ((len = write(lp->log_fd, buf, resid)) <= 0)
1207c478bd9Sstevel@tonic-gate break;
1217c478bd9Sstevel@tonic-gate
1227c478bd9Sstevel@tonic-gate resid -= len;
1237c478bd9Sstevel@tonic-gate buf = (char *)buf + len;
1247c478bd9Sstevel@tonic-gate }
1257c478bd9Sstevel@tonic-gate
1267c478bd9Sstevel@tonic-gate if (resid == n && n != 0)
1277c478bd9Sstevel@tonic-gate return (-1);
1287c478bd9Sstevel@tonic-gate
1297c478bd9Sstevel@tonic-gate return (n - resid);
1307c478bd9Sstevel@tonic-gate }
1317c478bd9Sstevel@tonic-gate
1327c478bd9Sstevel@tonic-gate static int
fmd_log_write_hdr(fmd_log_t * lp,const char * tag)1337c478bd9Sstevel@tonic-gate fmd_log_write_hdr(fmd_log_t *lp, const char *tag)
1347c478bd9Sstevel@tonic-gate {
1357ee93e3bSdilpreet ea_object_t hdr, toc, i0, i1, i2, i3, i4, i5, i6;
1367c478bd9Sstevel@tonic-gate const char *osrel, *osver, *plat;
1377c478bd9Sstevel@tonic-gate off64_t off = 0;
1387c478bd9Sstevel@tonic-gate int err = 0;
1397ee93e3bSdilpreet uuid_t uuid;
1407c478bd9Sstevel@tonic-gate
1417c478bd9Sstevel@tonic-gate (void) fmd_conf_getprop(fmd.d_conf, "osrelease", &osrel);
1427c478bd9Sstevel@tonic-gate (void) fmd_conf_getprop(fmd.d_conf, "osversion", &osver);
1437c478bd9Sstevel@tonic-gate (void) fmd_conf_getprop(fmd.d_conf, "platform", &plat);
1447ee93e3bSdilpreet (void) fmd_conf_getprop(fmd.d_conf, "uuidlen", &lp->log_uuidlen);
1457ee93e3bSdilpreet
1467ee93e3bSdilpreet lp->log_uuid = fmd_zalloc(lp->log_uuidlen + 1, FMD_SLEEP);
1477ee93e3bSdilpreet uuid_generate(uuid);
1487ee93e3bSdilpreet uuid_unparse(uuid, lp->log_uuid);
1497c478bd9Sstevel@tonic-gate
1507c478bd9Sstevel@tonic-gate err |= ea_set_group(&hdr, CAT_FMA_GROUP);
1517c478bd9Sstevel@tonic-gate err |= ea_set_group(&toc, CAT_FMA_GROUP);
1527c478bd9Sstevel@tonic-gate
1537c478bd9Sstevel@tonic-gate err |= ea_set_item(&i0, CAT_FMA_LABEL, tag, 0);
1547c478bd9Sstevel@tonic-gate err |= ea_set_item(&i1, CAT_FMA_VERSION, fmd.d_version, 0);
1557c478bd9Sstevel@tonic-gate err |= ea_set_item(&i2, CAT_FMA_OSREL, osrel, 0);
1567c478bd9Sstevel@tonic-gate err |= ea_set_item(&i3, CAT_FMA_OSVER, osver, 0);
1577c478bd9Sstevel@tonic-gate err |= ea_set_item(&i4, CAT_FMA_PLAT, plat, 0);
1587ee93e3bSdilpreet err |= ea_set_item(&i5, CAT_FMA_UUID, lp->log_uuid, 0);
1597ee93e3bSdilpreet err |= ea_set_item(&i6, CAT_FMA_OFFSET, &off, 0);
1607c478bd9Sstevel@tonic-gate
1617c478bd9Sstevel@tonic-gate (void) ea_attach_to_group(&hdr, &i0);
1627c478bd9Sstevel@tonic-gate (void) ea_attach_to_group(&hdr, &i1);
1637c478bd9Sstevel@tonic-gate (void) ea_attach_to_group(&hdr, &i2);
1647c478bd9Sstevel@tonic-gate (void) ea_attach_to_group(&hdr, &i3);
1657c478bd9Sstevel@tonic-gate (void) ea_attach_to_group(&hdr, &i4);
1667ee93e3bSdilpreet (void) ea_attach_to_group(&hdr, &i5);
1677ee93e3bSdilpreet (void) ea_attach_to_group(&toc, &i6);
1687c478bd9Sstevel@tonic-gate
1697c478bd9Sstevel@tonic-gate if (err == 0) {
1707c478bd9Sstevel@tonic-gate size_t hdr_size = ea_pack_object(&hdr, NULL, 0);
1717c478bd9Sstevel@tonic-gate size_t toc_size = ea_pack_object(&toc, NULL, 0);
1727c478bd9Sstevel@tonic-gate
1737c478bd9Sstevel@tonic-gate size_t size = hdr_size + toc_size;
1747c478bd9Sstevel@tonic-gate void *buf = fmd_alloc(size, FMD_SLEEP);
1757c478bd9Sstevel@tonic-gate
1767c478bd9Sstevel@tonic-gate (void) ea_pack_object(&hdr, buf, hdr_size);
1777c478bd9Sstevel@tonic-gate (void) ea_pack_object(&toc, (char *)buf + hdr_size, toc_size);
1787c478bd9Sstevel@tonic-gate
1797c478bd9Sstevel@tonic-gate if ((lp->log_off = lseek64(lp->log_fd, 0, SEEK_END)) == -1L)
1807c478bd9Sstevel@tonic-gate fmd_panic("failed to seek log %s", lp->log_name);
1817c478bd9Sstevel@tonic-gate
1827c478bd9Sstevel@tonic-gate if (fmd_log_write(lp, buf, size) != size)
1837c478bd9Sstevel@tonic-gate err = errno; /* save errno for fmd_set_errno() below */
1847c478bd9Sstevel@tonic-gate
1857c478bd9Sstevel@tonic-gate fmd_free(buf, size);
1867c478bd9Sstevel@tonic-gate
1877c478bd9Sstevel@tonic-gate lp->log_toc = lp->log_off + hdr_size;
1887c478bd9Sstevel@tonic-gate lp->log_beg = lp->log_off + hdr_size + toc_size;
1897c478bd9Sstevel@tonic-gate lp->log_off = lp->log_off + hdr_size + toc_size;
1907c478bd9Sstevel@tonic-gate
1917c478bd9Sstevel@tonic-gate if (lp->log_off != lseek64(lp->log_fd, 0, SEEK_END))
1927c478bd9Sstevel@tonic-gate fmd_panic("eof off != log_off 0x%llx\n", lp->log_off);
1937c478bd9Sstevel@tonic-gate } else
1947c478bd9Sstevel@tonic-gate err = EFMD_LOG_EXACCT;
1957c478bd9Sstevel@tonic-gate
1967c478bd9Sstevel@tonic-gate (void) ea_free_item(&i0, EUP_ALLOC);
1977c478bd9Sstevel@tonic-gate (void) ea_free_item(&i1, EUP_ALLOC);
1987c478bd9Sstevel@tonic-gate (void) ea_free_item(&i2, EUP_ALLOC);
1997c478bd9Sstevel@tonic-gate (void) ea_free_item(&i3, EUP_ALLOC);
2007c478bd9Sstevel@tonic-gate (void) ea_free_item(&i4, EUP_ALLOC);
2017c478bd9Sstevel@tonic-gate (void) ea_free_item(&i5, EUP_ALLOC);
2027ee93e3bSdilpreet (void) ea_free_item(&i6, EUP_ALLOC);
2037c478bd9Sstevel@tonic-gate
2047c478bd9Sstevel@tonic-gate return (err ? fmd_set_errno(err) : 0);
2057c478bd9Sstevel@tonic-gate }
2067c478bd9Sstevel@tonic-gate
2077c478bd9Sstevel@tonic-gate static int
fmd_log_check_err(fmd_log_t * lp,int err,const char * msg)2087c478bd9Sstevel@tonic-gate fmd_log_check_err(fmd_log_t *lp, int err, const char *msg)
2097c478bd9Sstevel@tonic-gate {
2107c478bd9Sstevel@tonic-gate int eaerr = ea_error();
2117c478bd9Sstevel@tonic-gate char buf[BUFSIZ];
2127c478bd9Sstevel@tonic-gate
2137c478bd9Sstevel@tonic-gate (void) snprintf(buf, sizeof (buf), "%s: %s: %s\n",
2147c478bd9Sstevel@tonic-gate lp->log_name, msg, eaerr != EXR_OK ?
2157c478bd9Sstevel@tonic-gate fmd_ea_strerror(eaerr) : "catalog tag mismatch");
2167c478bd9Sstevel@tonic-gate
2177c478bd9Sstevel@tonic-gate fmd_error(err, buf);
2187c478bd9Sstevel@tonic-gate return (fmd_set_errno(err));
2197c478bd9Sstevel@tonic-gate }
2207c478bd9Sstevel@tonic-gate
2217c478bd9Sstevel@tonic-gate static int
fmd_log_check_hdr(fmd_log_t * lp,const char * tag)2227c478bd9Sstevel@tonic-gate fmd_log_check_hdr(fmd_log_t *lp, const char *tag)
2237c478bd9Sstevel@tonic-gate {
2247c478bd9Sstevel@tonic-gate int got_version = 0, got_label = 0;
2257c478bd9Sstevel@tonic-gate ea_object_t *grp, *obj;
2267c478bd9Sstevel@tonic-gate off64_t hdr_off, hdr_size;
2277c478bd9Sstevel@tonic-gate int dvers, fvers;
2287c478bd9Sstevel@tonic-gate const char *p;
2297c478bd9Sstevel@tonic-gate
2307c478bd9Sstevel@tonic-gate ea_clear(&lp->log_ea); /* resync exacct file */
2317c478bd9Sstevel@tonic-gate
2327c478bd9Sstevel@tonic-gate if ((hdr_off = lseek64(lp->log_fd, 0, SEEK_CUR)) == -1L)
2337c478bd9Sstevel@tonic-gate fmd_panic("failed to seek log %s", lp->log_name);
2347c478bd9Sstevel@tonic-gate
2357c478bd9Sstevel@tonic-gate /*
2367c478bd9Sstevel@tonic-gate * Read the first group of log meta-data: the write-once read-only
2377c478bd9Sstevel@tonic-gate * file header. We read all records in this group, ignoring all but
2387c478bd9Sstevel@tonic-gate * the VERSION and LABEL, which are required and must be verified.
2397c478bd9Sstevel@tonic-gate */
2407c478bd9Sstevel@tonic-gate if ((grp = ea_get_object_tree(&lp->log_ea, 1)) == NULL ||
2417c478bd9Sstevel@tonic-gate grp->eo_catalog != CAT_FMA_GROUP) {
2427c478bd9Sstevel@tonic-gate ea_free_object(grp, EUP_ALLOC);
2437c478bd9Sstevel@tonic-gate return (fmd_log_check_err(lp, EFMD_LOG_INVAL,
2447c478bd9Sstevel@tonic-gate "invalid fma hdr record group"));
2457c478bd9Sstevel@tonic-gate }
2467c478bd9Sstevel@tonic-gate
2477c478bd9Sstevel@tonic-gate for (obj = grp->eo_group.eg_objs; obj != NULL; obj = obj->eo_next) {
2487c478bd9Sstevel@tonic-gate switch (obj->eo_catalog) {
2497c478bd9Sstevel@tonic-gate case CAT_FMA_VERSION:
2507c478bd9Sstevel@tonic-gate for (dvers = 0, p = fmd.d_version;
2517c478bd9Sstevel@tonic-gate *p != '\0'; p++) {
2527c478bd9Sstevel@tonic-gate if (isdigit(*p))
2537c478bd9Sstevel@tonic-gate dvers = dvers * 10 + (*p - '0');
2547c478bd9Sstevel@tonic-gate else
2557c478bd9Sstevel@tonic-gate break;
2567c478bd9Sstevel@tonic-gate }
2577c478bd9Sstevel@tonic-gate
2587c478bd9Sstevel@tonic-gate for (fvers = 0, p = obj->eo_item.ei_string;
2597c478bd9Sstevel@tonic-gate *p != '\0'; p++) {
2607c478bd9Sstevel@tonic-gate if (isdigit(*p))
2617c478bd9Sstevel@tonic-gate fvers = fvers * 10 + (*p - '0');
2627c478bd9Sstevel@tonic-gate else
2637c478bd9Sstevel@tonic-gate break;
2647c478bd9Sstevel@tonic-gate }
2657c478bd9Sstevel@tonic-gate
2667c478bd9Sstevel@tonic-gate if (fvers > dvers) {
2677c478bd9Sstevel@tonic-gate fmd_error(EFMD_LOG_INVAL, "%s: log version "
2687c478bd9Sstevel@tonic-gate "%s is not supported by this daemon\n",
2697c478bd9Sstevel@tonic-gate lp->log_name, obj->eo_item.ei_string);
2707c478bd9Sstevel@tonic-gate ea_free_object(grp, EUP_ALLOC);
2717c478bd9Sstevel@tonic-gate return (fmd_set_errno(EFMD_LOG_VERSION));
2727c478bd9Sstevel@tonic-gate }
2737c478bd9Sstevel@tonic-gate
2747c478bd9Sstevel@tonic-gate got_version++;
2757c478bd9Sstevel@tonic-gate break;
2767c478bd9Sstevel@tonic-gate
2777c478bd9Sstevel@tonic-gate case CAT_FMA_LABEL:
2787c478bd9Sstevel@tonic-gate if (strcmp(obj->eo_item.ei_string, tag) != 0) {
2797c478bd9Sstevel@tonic-gate fmd_error(EFMD_LOG_INVAL, "%s: log tag '%s' "
2807c478bd9Sstevel@tonic-gate "does not matched expected tag '%s'\n",
2817c478bd9Sstevel@tonic-gate lp->log_name, obj->eo_item.ei_string, tag);
2827c478bd9Sstevel@tonic-gate ea_free_object(grp, EUP_ALLOC);
2837c478bd9Sstevel@tonic-gate return (fmd_set_errno(EFMD_LOG_INVAL));
2847c478bd9Sstevel@tonic-gate }
2857c478bd9Sstevel@tonic-gate got_label++;
2867c478bd9Sstevel@tonic-gate break;
2877ee93e3bSdilpreet case CAT_FMA_UUID:
2887ee93e3bSdilpreet lp->log_uuid = fmd_strdup(obj->eo_item.ei_string,
2897ee93e3bSdilpreet FMD_SLEEP);
2907ee93e3bSdilpreet lp->log_uuidlen = strlen(lp->log_uuid);
2917ee93e3bSdilpreet break;
2927c478bd9Sstevel@tonic-gate }
2937c478bd9Sstevel@tonic-gate }
2947c478bd9Sstevel@tonic-gate
2957c478bd9Sstevel@tonic-gate hdr_size = ea_pack_object(grp, NULL, 0);
2967c478bd9Sstevel@tonic-gate ea_free_object(grp, EUP_ALLOC);
2977c478bd9Sstevel@tonic-gate
2987c478bd9Sstevel@tonic-gate if (!got_version || !got_label) {
2997c478bd9Sstevel@tonic-gate fmd_error(EFMD_LOG_INVAL, "%s: fmd hdr record group did not "
3007c478bd9Sstevel@tonic-gate "include mandatory version and/or label\n", lp->log_name);
3017c478bd9Sstevel@tonic-gate return (fmd_set_errno(EFMD_LOG_INVAL));
3027c478bd9Sstevel@tonic-gate }
3037c478bd9Sstevel@tonic-gate
3047c478bd9Sstevel@tonic-gate /*
3057c478bd9Sstevel@tonic-gate * Read the second group of log meta-data: the table of contents. We
3067c478bd9Sstevel@tonic-gate * expect this group to contain an OFFSET object indicating the current
3077c478bd9Sstevel@tonic-gate * value of log_skip. We save this in our fmd_log_t and then return.
3087c478bd9Sstevel@tonic-gate */
3097c478bd9Sstevel@tonic-gate if ((grp = ea_get_object_tree(&lp->log_ea, 1)) == NULL ||
3107c478bd9Sstevel@tonic-gate grp->eo_catalog != CAT_FMA_GROUP || grp->eo_group.eg_nobjs < 1 ||
3117c478bd9Sstevel@tonic-gate grp->eo_group.eg_objs->eo_catalog != CAT_FMA_OFFSET) {
3127c478bd9Sstevel@tonic-gate ea_free_object(grp, EUP_ALLOC);
3137c478bd9Sstevel@tonic-gate return (fmd_log_check_err(lp, EFMD_LOG_INVAL,
3147c478bd9Sstevel@tonic-gate "invalid fma toc record group"));
3157c478bd9Sstevel@tonic-gate }
3167c478bd9Sstevel@tonic-gate
3177c478bd9Sstevel@tonic-gate lp->log_toc = hdr_off + hdr_size;
3187c478bd9Sstevel@tonic-gate lp->log_beg = hdr_off + hdr_size + ea_pack_object(grp, NULL, 0);
3197c478bd9Sstevel@tonic-gate lp->log_off = lseek64(lp->log_fd, 0, SEEK_END);
3207c478bd9Sstevel@tonic-gate lp->log_skip = grp->eo_group.eg_objs->eo_item.ei_uint64;
3217c478bd9Sstevel@tonic-gate
3227c478bd9Sstevel@tonic-gate if (lp->log_skip > lp->log_off) {
3237c478bd9Sstevel@tonic-gate fmd_error(EFMD_LOG_INVAL, "%s: skip %llx exceeds file size; "
3247c478bd9Sstevel@tonic-gate "resetting to zero\n", lp->log_name, lp->log_skip);
3257c478bd9Sstevel@tonic-gate lp->log_skip = 0;
3267c478bd9Sstevel@tonic-gate }
3277c478bd9Sstevel@tonic-gate
3287c478bd9Sstevel@tonic-gate ea_free_object(grp, EUP_ALLOC);
3297c478bd9Sstevel@tonic-gate return (0);
3307c478bd9Sstevel@tonic-gate }
3317c478bd9Sstevel@tonic-gate
3327c478bd9Sstevel@tonic-gate static int
fmd_log_open_exacct(fmd_log_t * lp,int aflags,int oflags)3337c478bd9Sstevel@tonic-gate fmd_log_open_exacct(fmd_log_t *lp, int aflags, int oflags)
3347c478bd9Sstevel@tonic-gate {
3357c478bd9Sstevel@tonic-gate int fd = dup(lp->log_fd);
3367c478bd9Sstevel@tonic-gate const char *creator;
3377c478bd9Sstevel@tonic-gate
3387c478bd9Sstevel@tonic-gate (void) fmd_conf_getprop(fmd.d_conf, "log.creator", &creator);
3397c478bd9Sstevel@tonic-gate
3407c478bd9Sstevel@tonic-gate if (ea_fdopen(&lp->log_ea, fd, creator, aflags, oflags) != 0) {
3417c478bd9Sstevel@tonic-gate fmd_error(EFMD_LOG_EXACCT, "%s: failed to open log file: %s\n",
3427c478bd9Sstevel@tonic-gate lp->log_name, fmd_ea_strerror(ea_error()));
3437c478bd9Sstevel@tonic-gate (void) close(fd);
3447c478bd9Sstevel@tonic-gate return (fmd_set_errno(EFMD_LOG_EXACCT));
3457c478bd9Sstevel@tonic-gate }
3467c478bd9Sstevel@tonic-gate
3477c478bd9Sstevel@tonic-gate lp->log_flags |= FMD_LF_EAOPEN;
3487c478bd9Sstevel@tonic-gate return (0);
3497c478bd9Sstevel@tonic-gate }
3507c478bd9Sstevel@tonic-gate
3517c478bd9Sstevel@tonic-gate static fmd_log_t *
fmd_log_xopen(const char * root,const char * name,const char * tag,int oflags)3527c478bd9Sstevel@tonic-gate fmd_log_xopen(const char *root, const char *name, const char *tag, int oflags)
3537c478bd9Sstevel@tonic-gate {
3547c478bd9Sstevel@tonic-gate fmd_log_t *lp = fmd_zalloc(sizeof (fmd_log_t), FMD_SLEEP);
3557c478bd9Sstevel@tonic-gate
3567c478bd9Sstevel@tonic-gate char buf[PATH_MAX];
357*9dd0f810Scindi char *slash = "/";
3587c478bd9Sstevel@tonic-gate size_t len;
3597c478bd9Sstevel@tonic-gate int err;
3607c478bd9Sstevel@tonic-gate
3617c478bd9Sstevel@tonic-gate (void) pthread_mutex_init(&lp->log_lock, NULL);
3627c478bd9Sstevel@tonic-gate (void) pthread_cond_init(&lp->log_cv, NULL);
3637c478bd9Sstevel@tonic-gate (void) pthread_mutex_lock(&lp->log_lock);
3647c478bd9Sstevel@tonic-gate
365*9dd0f810Scindi if (strcmp(root, "") == 0)
366*9dd0f810Scindi slash = "";
367*9dd0f810Scindi len = strlen(root) + strlen(name) + strlen(slash) + 1; /* for "\0" */
3687c478bd9Sstevel@tonic-gate lp->log_name = fmd_alloc(len, FMD_SLEEP);
369*9dd0f810Scindi (void) snprintf(lp->log_name, len, "%s%s%s", root, slash, name);
3707c478bd9Sstevel@tonic-gate lp->log_tag = fmd_strdup(tag, FMD_SLEEP);
3717c478bd9Sstevel@tonic-gate (void) fmd_conf_getprop(fmd.d_conf, "log.minfree", &lp->log_minfree);
3727c478bd9Sstevel@tonic-gate
3737c478bd9Sstevel@tonic-gate if (strcmp(lp->log_tag, FMD_LOG_ERROR) == 0)
3747c478bd9Sstevel@tonic-gate lp->log_flags |= FMD_LF_REPLAY;
3757c478bd9Sstevel@tonic-gate
376d9638e54Smws if (strcmp(lp->log_tag, FMD_LOG_XPRT) == 0)
377d9638e54Smws oflags &= ~O_SYNC;
378d9638e54Smws
3797c478bd9Sstevel@tonic-gate top:
3807c478bd9Sstevel@tonic-gate if ((lp->log_fd = open64(lp->log_name, oflags, 0644)) == -1 ||
3817c478bd9Sstevel@tonic-gate fstat64(lp->log_fd, &lp->log_stat) == -1) {
3827c478bd9Sstevel@tonic-gate fmd_error(EFMD_LOG_OPEN, "failed to open log %s", lp->log_name);
3837c478bd9Sstevel@tonic-gate fmd_log_close(lp);
3847c478bd9Sstevel@tonic-gate return (NULL);
3857c478bd9Sstevel@tonic-gate }
3867c478bd9Sstevel@tonic-gate
3877c478bd9Sstevel@tonic-gate /*
3887c478bd9Sstevel@tonic-gate * If our open() created the log file, use libexacct to write a header
3897c478bd9Sstevel@tonic-gate * and position the file just after the header (EO_TAIL). If the log
3907c478bd9Sstevel@tonic-gate * file already existed, use libexacct to validate the header and again
3917c478bd9Sstevel@tonic-gate * position the file just after the header (EO_HEAD). Note that we lie
3927c478bd9Sstevel@tonic-gate * to libexacct about 'oflags' in order to achieve the desired result.
3937c478bd9Sstevel@tonic-gate */
3947c478bd9Sstevel@tonic-gate if (lp->log_stat.st_size == 0) {
3957c478bd9Sstevel@tonic-gate err = fmd_log_open_exacct(lp, EO_VALID_HDR | EO_TAIL,
3967c478bd9Sstevel@tonic-gate O_CREAT | O_WRONLY) || fmd_log_write_hdr(lp, tag);
3977c478bd9Sstevel@tonic-gate } else {
3987c478bd9Sstevel@tonic-gate err = fmd_log_open_exacct(lp, EO_VALID_HDR | EO_HEAD,
3997c478bd9Sstevel@tonic-gate O_RDONLY) || fmd_log_check_hdr(lp, tag);
4007c478bd9Sstevel@tonic-gate }
4017c478bd9Sstevel@tonic-gate
4027c478bd9Sstevel@tonic-gate /*
4037c478bd9Sstevel@tonic-gate * If ea_fdopen() failed and the log was pre-existing, attempt to move
4047c478bd9Sstevel@tonic-gate * it aside and start a new one. If we created the log but failed to
4057c478bd9Sstevel@tonic-gate * initialize it, then we have no choice but to give up (e.g. EROFS).
4067c478bd9Sstevel@tonic-gate */
4077c478bd9Sstevel@tonic-gate if (err) {
4087c478bd9Sstevel@tonic-gate fmd_error(EFMD_LOG_OPEN,
4097c478bd9Sstevel@tonic-gate "failed to initialize log %s", lp->log_name);
4107c478bd9Sstevel@tonic-gate
4117c478bd9Sstevel@tonic-gate if (lp->log_flags & FMD_LF_EAOPEN) {
4127c478bd9Sstevel@tonic-gate lp->log_flags &= ~FMD_LF_EAOPEN;
4137c478bd9Sstevel@tonic-gate (void) ea_close(&lp->log_ea);
4147c478bd9Sstevel@tonic-gate }
4157c478bd9Sstevel@tonic-gate
4167c478bd9Sstevel@tonic-gate (void) close(lp->log_fd);
4177c478bd9Sstevel@tonic-gate lp->log_fd = -1;
4187c478bd9Sstevel@tonic-gate
4197c478bd9Sstevel@tonic-gate if (lp->log_stat.st_size != 0 && snprintf(buf,
4207c478bd9Sstevel@tonic-gate sizeof (buf), "%s-", lp->log_name) < PATH_MAX &&
4217c478bd9Sstevel@tonic-gate rename(lp->log_name, buf) == 0) {
4227c478bd9Sstevel@tonic-gate TRACE((FMD_DBG_LOG, "mv %s to %s", lp->log_name, buf));
4237c478bd9Sstevel@tonic-gate if (oflags & O_CREAT)
4247c478bd9Sstevel@tonic-gate goto top;
4257c478bd9Sstevel@tonic-gate }
4267c478bd9Sstevel@tonic-gate
4277c478bd9Sstevel@tonic-gate fmd_log_close(lp);
4287c478bd9Sstevel@tonic-gate return (NULL);
4297c478bd9Sstevel@tonic-gate }
4307c478bd9Sstevel@tonic-gate
4317c478bd9Sstevel@tonic-gate lp->log_refs++;
4327c478bd9Sstevel@tonic-gate (void) pthread_mutex_unlock(&lp->log_lock);
4337c478bd9Sstevel@tonic-gate
4347c478bd9Sstevel@tonic-gate return (lp);
4357c478bd9Sstevel@tonic-gate }
4367c478bd9Sstevel@tonic-gate
4377c478bd9Sstevel@tonic-gate fmd_log_t *
fmd_log_tryopen(const char * root,const char * name,const char * tag)4387c478bd9Sstevel@tonic-gate fmd_log_tryopen(const char *root, const char *name, const char *tag)
4397c478bd9Sstevel@tonic-gate {
4407c478bd9Sstevel@tonic-gate return (fmd_log_xopen(root, name, tag, O_RDWR | O_SYNC));
4417c478bd9Sstevel@tonic-gate }
4427c478bd9Sstevel@tonic-gate
4437c478bd9Sstevel@tonic-gate fmd_log_t *
fmd_log_open(const char * root,const char * name,const char * tag)4447c478bd9Sstevel@tonic-gate fmd_log_open(const char *root, const char *name, const char *tag)
4457c478bd9Sstevel@tonic-gate {
4467c478bd9Sstevel@tonic-gate return (fmd_log_xopen(root, name, tag, O_RDWR | O_CREAT | O_SYNC));
4477c478bd9Sstevel@tonic-gate }
4487c478bd9Sstevel@tonic-gate
4497c478bd9Sstevel@tonic-gate void
fmd_log_close(fmd_log_t * lp)4507c478bd9Sstevel@tonic-gate fmd_log_close(fmd_log_t *lp)
4517c478bd9Sstevel@tonic-gate {
4527c478bd9Sstevel@tonic-gate ASSERT(MUTEX_HELD(&lp->log_lock));
4537c478bd9Sstevel@tonic-gate ASSERT(lp->log_refs == 0);
4547c478bd9Sstevel@tonic-gate
4557c478bd9Sstevel@tonic-gate if ((lp->log_flags & FMD_LF_EAOPEN) && ea_close(&lp->log_ea) != 0) {
4567c478bd9Sstevel@tonic-gate fmd_error(EFMD_LOG_CLOSE, "failed to close log %s: %s\n",
4577c478bd9Sstevel@tonic-gate lp->log_name, fmd_ea_strerror(ea_error()));
4587c478bd9Sstevel@tonic-gate }
4597c478bd9Sstevel@tonic-gate
4607c478bd9Sstevel@tonic-gate if (lp->log_fd >= 0 && close(lp->log_fd) != 0) {
4617c478bd9Sstevel@tonic-gate fmd_error(EFMD_LOG_CLOSE,
4627c478bd9Sstevel@tonic-gate "failed to close log %s", lp->log_name);
4637c478bd9Sstevel@tonic-gate }
4647c478bd9Sstevel@tonic-gate
4657c478bd9Sstevel@tonic-gate fmd_strfree(lp->log_name);
4667c478bd9Sstevel@tonic-gate fmd_strfree(lp->log_tag);
4677ee93e3bSdilpreet if (lp->log_uuid != NULL)
4687ee93e3bSdilpreet fmd_free(lp->log_uuid, lp->log_uuidlen + 1);
4697c478bd9Sstevel@tonic-gate
4707c478bd9Sstevel@tonic-gate fmd_free(lp, sizeof (fmd_log_t));
4717c478bd9Sstevel@tonic-gate }
4727c478bd9Sstevel@tonic-gate
4737c478bd9Sstevel@tonic-gate void
fmd_log_hold_pending(fmd_log_t * lp)4747c478bd9Sstevel@tonic-gate fmd_log_hold_pending(fmd_log_t *lp)
4757c478bd9Sstevel@tonic-gate {
4767c478bd9Sstevel@tonic-gate (void) pthread_mutex_lock(&lp->log_lock);
4777c478bd9Sstevel@tonic-gate
4787c478bd9Sstevel@tonic-gate lp->log_refs++;
4797c478bd9Sstevel@tonic-gate ASSERT(lp->log_refs != 0);
4807c478bd9Sstevel@tonic-gate
4817c478bd9Sstevel@tonic-gate if (lp->log_flags & FMD_LF_REPLAY) {
4827c478bd9Sstevel@tonic-gate lp->log_pending++;
4837c478bd9Sstevel@tonic-gate ASSERT(lp->log_pending != 0);
4847c478bd9Sstevel@tonic-gate }
4857c478bd9Sstevel@tonic-gate
4867c478bd9Sstevel@tonic-gate (void) pthread_mutex_unlock(&lp->log_lock);
4877c478bd9Sstevel@tonic-gate }
4887c478bd9Sstevel@tonic-gate
4897c478bd9Sstevel@tonic-gate void
fmd_log_hold(fmd_log_t * lp)4907c478bd9Sstevel@tonic-gate fmd_log_hold(fmd_log_t *lp)
4917c478bd9Sstevel@tonic-gate {
4927c478bd9Sstevel@tonic-gate (void) pthread_mutex_lock(&lp->log_lock);
4937c478bd9Sstevel@tonic-gate lp->log_refs++;
4947c478bd9Sstevel@tonic-gate ASSERT(lp->log_refs != 0);
4957c478bd9Sstevel@tonic-gate (void) pthread_mutex_unlock(&lp->log_lock);
4967c478bd9Sstevel@tonic-gate }
4977c478bd9Sstevel@tonic-gate
4987c478bd9Sstevel@tonic-gate void
fmd_log_rele(fmd_log_t * lp)4997c478bd9Sstevel@tonic-gate fmd_log_rele(fmd_log_t *lp)
5007c478bd9Sstevel@tonic-gate {
5017c478bd9Sstevel@tonic-gate (void) pthread_mutex_lock(&lp->log_lock);
5027c478bd9Sstevel@tonic-gate ASSERT(lp->log_refs != 0);
5037c478bd9Sstevel@tonic-gate
5047c478bd9Sstevel@tonic-gate if (--lp->log_refs == 0)
5057c478bd9Sstevel@tonic-gate fmd_log_close(lp);
5067c478bd9Sstevel@tonic-gate else
5077c478bd9Sstevel@tonic-gate (void) pthread_mutex_unlock(&lp->log_lock);
5087c478bd9Sstevel@tonic-gate }
5097c478bd9Sstevel@tonic-gate
5107c478bd9Sstevel@tonic-gate void
fmd_log_append(fmd_log_t * lp,fmd_event_t * e,fmd_case_t * cp)5117c478bd9Sstevel@tonic-gate fmd_log_append(fmd_log_t *lp, fmd_event_t *e, fmd_case_t *cp)
5127c478bd9Sstevel@tonic-gate {
5137c478bd9Sstevel@tonic-gate fmd_event_impl_t *ep = (fmd_event_impl_t *)e;
5147c478bd9Sstevel@tonic-gate fmd_case_impl_t *cip = (fmd_case_impl_t *)cp;
5157c478bd9Sstevel@tonic-gate int err = 0;
5167c478bd9Sstevel@tonic-gate
5177c478bd9Sstevel@tonic-gate ea_object_t grp0, grp1, i0, i1, i2, *items;
5187ee93e3bSdilpreet ea_object_t **fe = NULL;
5197ee93e3bSdilpreet size_t nvsize, easize, itsize, frsize;
5207c478bd9Sstevel@tonic-gate char *nvbuf, *eabuf;
5217c478bd9Sstevel@tonic-gate statvfs64_t stv;
5227c478bd9Sstevel@tonic-gate
5237c478bd9Sstevel@tonic-gate (void) pthread_mutex_lock(&ep->ev_lock);
5247c478bd9Sstevel@tonic-gate
5257c478bd9Sstevel@tonic-gate ASSERT(ep->ev_flags & FMD_EVF_VOLATILE);
5267c478bd9Sstevel@tonic-gate ASSERT(ep->ev_log == NULL);
5277c478bd9Sstevel@tonic-gate
5287c478bd9Sstevel@tonic-gate (void) nvlist_size(ep->ev_nvl, &nvsize, NV_ENCODE_XDR);
5297c478bd9Sstevel@tonic-gate nvbuf = fmd_alloc(nvsize, FMD_SLEEP);
5307c478bd9Sstevel@tonic-gate (void) nvlist_pack(ep->ev_nvl, &nvbuf, &nvsize, NV_ENCODE_XDR, 0);
5317c478bd9Sstevel@tonic-gate
5327c478bd9Sstevel@tonic-gate if (lp->log_flags & FMD_LF_REPLAY)
5337c478bd9Sstevel@tonic-gate err |= ea_set_group(&grp0, CAT_FMA_RGROUP);
5347c478bd9Sstevel@tonic-gate else
5357c478bd9Sstevel@tonic-gate err |= ea_set_group(&grp0, CAT_FMA_GROUP);
5367c478bd9Sstevel@tonic-gate
5377c478bd9Sstevel@tonic-gate err |= ea_set_item(&i0, CAT_FMA_TODSEC, &ep->ev_time.ftv_sec, 0);
5387c478bd9Sstevel@tonic-gate err |= ea_set_item(&i1, CAT_FMA_TODNSEC, &ep->ev_time.ftv_nsec, 0);
5397c478bd9Sstevel@tonic-gate err |= ea_set_item(&i2, CAT_FMA_NVLIST, nvbuf, nvsize);
5407c478bd9Sstevel@tonic-gate
5417c478bd9Sstevel@tonic-gate if (err != 0) {
5427c478bd9Sstevel@tonic-gate (void) pthread_mutex_unlock(&ep->ev_lock);
5437c478bd9Sstevel@tonic-gate err = EFMD_LOG_EXACCT;
5447c478bd9Sstevel@tonic-gate goto exerr;
5457c478bd9Sstevel@tonic-gate }
5467c478bd9Sstevel@tonic-gate
5477c478bd9Sstevel@tonic-gate (void) ea_attach_to_group(&grp0, &i0);
5487c478bd9Sstevel@tonic-gate (void) ea_attach_to_group(&grp0, &i1);
5497c478bd9Sstevel@tonic-gate (void) ea_attach_to_group(&grp0, &i2);
5507c478bd9Sstevel@tonic-gate
5517c478bd9Sstevel@tonic-gate /*
5527c478bd9Sstevel@tonic-gate * If this event has a case associated with it (i.e. it is a list),
5537c478bd9Sstevel@tonic-gate * then allocate a block of ea_object_t's and fill in a group for
5547c478bd9Sstevel@tonic-gate * each event saved in the case's item list. For each such group,
5557c478bd9Sstevel@tonic-gate * we attach it to grp1, which in turn will be attached to grp0.
5567c478bd9Sstevel@tonic-gate */
5577c478bd9Sstevel@tonic-gate if (cp != NULL) {
5587ee93e3bSdilpreet ea_object_t *egrp, *ip, **fp;
5597c478bd9Sstevel@tonic-gate fmd_event_impl_t *eip;
5607c478bd9Sstevel@tonic-gate fmd_case_item_t *cit;
5617c478bd9Sstevel@tonic-gate
5627c478bd9Sstevel@tonic-gate (void) ea_set_group(&grp1, CAT_FMA_GROUP);
5637ee93e3bSdilpreet frsize = sizeof (ea_object_t *) * cip->ci_nitems;
5647c478bd9Sstevel@tonic-gate itsize = sizeof (ea_object_t) * cip->ci_nitems * 5;
5657c478bd9Sstevel@tonic-gate items = ip = fmd_alloc(itsize, FMD_SLEEP);
5667c478bd9Sstevel@tonic-gate
5677c478bd9Sstevel@tonic-gate for (cit = cip->ci_items; cit != NULL; cit = cit->cit_next) {
5687c478bd9Sstevel@tonic-gate major_t maj;
5697c478bd9Sstevel@tonic-gate minor_t min;
5707c478bd9Sstevel@tonic-gate
5717c478bd9Sstevel@tonic-gate eip = (fmd_event_impl_t *)cit->cit_event;
5727c478bd9Sstevel@tonic-gate
5737c478bd9Sstevel@tonic-gate if (eip->ev_log == NULL)
5747c478bd9Sstevel@tonic-gate continue; /* event was never logged */
5757c478bd9Sstevel@tonic-gate
5767c478bd9Sstevel@tonic-gate maj = major(eip->ev_log->log_stat.st_dev);
5777c478bd9Sstevel@tonic-gate min = minor(eip->ev_log->log_stat.st_dev);
5787c478bd9Sstevel@tonic-gate
5797c478bd9Sstevel@tonic-gate (void) ea_set_group(ip, CAT_FMA_GROUP);
5807c478bd9Sstevel@tonic-gate egrp = ip++; /* first obj is group */
5817c478bd9Sstevel@tonic-gate
5827ee93e3bSdilpreet /*
5837ee93e3bSdilpreet * If the event log file is in legacy format,
5847ee93e3bSdilpreet * then write the xref to the file in the legacy
5857ee93e3bSdilpreet * maj/min/inode method else write it using the
5867ee93e3bSdilpreet * file uuid.
5877ee93e3bSdilpreet */
5887ee93e3bSdilpreet if (eip->ev_log->log_uuid == NULL) {
5897c478bd9Sstevel@tonic-gate (void) ea_set_item(ip, CAT_FMA_MAJOR, &maj, 0);
5907c478bd9Sstevel@tonic-gate (void) ea_attach_to_group(egrp, ip++);
5917c478bd9Sstevel@tonic-gate (void) ea_set_item(ip, CAT_FMA_MINOR, &min, 0);
5927c478bd9Sstevel@tonic-gate (void) ea_attach_to_group(egrp, ip++);
5937c478bd9Sstevel@tonic-gate (void) ea_set_item(ip, CAT_FMA_INODE,
5947c478bd9Sstevel@tonic-gate &eip->ev_log->log_stat.st_ino, 0);
5957c478bd9Sstevel@tonic-gate (void) ea_attach_to_group(egrp, ip++);
5967ee93e3bSdilpreet } else {
5977ee93e3bSdilpreet if (ea_set_item(ip, CAT_FMA_UUID,
5987ee93e3bSdilpreet eip->ev_log->log_uuid, 0) == -1) {
5997ee93e3bSdilpreet err = EFMD_LOG_EXACCT;
6007ee93e3bSdilpreet goto exerrcp;
6017ee93e3bSdilpreet }
6027ee93e3bSdilpreet if (fe == NULL)
6037ee93e3bSdilpreet fe = fp = fmd_zalloc(frsize, FMD_SLEEP);
6047ee93e3bSdilpreet *fp++ = ip;
6057ee93e3bSdilpreet (void) ea_attach_to_group(egrp, ip++);
6067ee93e3bSdilpreet }
6077c478bd9Sstevel@tonic-gate (void) ea_set_item(ip, CAT_FMA_OFFSET, &eip->ev_off, 0);
6087c478bd9Sstevel@tonic-gate (void) ea_attach_to_group(egrp, ip++);
6097c478bd9Sstevel@tonic-gate (void) ea_attach_to_group(&grp1, egrp);
6107c478bd9Sstevel@tonic-gate }
6117c478bd9Sstevel@tonic-gate (void) ea_attach_to_group(&grp0, &grp1);
6127c478bd9Sstevel@tonic-gate }
6137c478bd9Sstevel@tonic-gate
6147c478bd9Sstevel@tonic-gate easize = ea_pack_object(&grp0, NULL, 0);
6157c478bd9Sstevel@tonic-gate eabuf = fmd_alloc(easize, FMD_SLEEP);
6167c478bd9Sstevel@tonic-gate (void) ea_pack_object(&grp0, eabuf, easize);
6177c478bd9Sstevel@tonic-gate
6187c478bd9Sstevel@tonic-gate /*
6197c478bd9Sstevel@tonic-gate * Before writing the record, check to see if this would cause the free
6207c478bd9Sstevel@tonic-gate * space in the filesystem to drop below our minfree threshold. If so,
6217c478bd9Sstevel@tonic-gate * don't bother attempting the write and instead pretend it failed. As
6227c478bd9Sstevel@tonic-gate * fmd(1M) runs as root, it will be able to access the space "reserved"
6237c478bd9Sstevel@tonic-gate * for root, and therefore can run the system of out of disk space in a
6247c478bd9Sstevel@tonic-gate * heavy error load situation, violating the basic design principle of
6257c478bd9Sstevel@tonic-gate * fmd(1M) that we don't want to make a bad situation even worse.
6267c478bd9Sstevel@tonic-gate */
6277c478bd9Sstevel@tonic-gate (void) pthread_mutex_lock(&lp->log_lock);
6287c478bd9Sstevel@tonic-gate
6297c478bd9Sstevel@tonic-gate if (lp->log_minfree != 0 && fstatvfs64(lp->log_fd, &stv) == 0 &&
6307c478bd9Sstevel@tonic-gate stv.f_bavail * stv.f_frsize < lp->log_minfree + easize) {
6317c478bd9Sstevel@tonic-gate
6327c478bd9Sstevel@tonic-gate TRACE((FMD_DBG_LOG, "append %s crosses minfree", lp->log_tag));
6337c478bd9Sstevel@tonic-gate err = EFMD_LOG_MINFREE;
6347c478bd9Sstevel@tonic-gate
6357c478bd9Sstevel@tonic-gate } else if (fmd_log_write(lp, eabuf, easize) == easize) {
6367c478bd9Sstevel@tonic-gate TRACE((FMD_DBG_LOG, "append %s %p off=0x%llx",
6377c478bd9Sstevel@tonic-gate lp->log_tag, (void *)ep, (u_longlong_t)lp->log_off));
6387c478bd9Sstevel@tonic-gate
6397c478bd9Sstevel@tonic-gate ep->ev_flags &= ~FMD_EVF_VOLATILE;
6407c478bd9Sstevel@tonic-gate ep->ev_log = lp;
6417c478bd9Sstevel@tonic-gate ep->ev_off = lp->log_off;
6427c478bd9Sstevel@tonic-gate ep->ev_len = easize;
6437c478bd9Sstevel@tonic-gate
6447c478bd9Sstevel@tonic-gate if (lp->log_flags & FMD_LF_REPLAY) {
6457c478bd9Sstevel@tonic-gate lp->log_pending++;
6467c478bd9Sstevel@tonic-gate ASSERT(lp->log_pending != 0);
6477c478bd9Sstevel@tonic-gate }
6487c478bd9Sstevel@tonic-gate
6497c478bd9Sstevel@tonic-gate lp->log_refs++;
6507c478bd9Sstevel@tonic-gate ASSERT(lp->log_refs != 0);
6517c478bd9Sstevel@tonic-gate lp->log_off += easize;
6527c478bd9Sstevel@tonic-gate } else {
6537c478bd9Sstevel@tonic-gate err = errno; /* save errno for fmd_error() call below */
6547c478bd9Sstevel@tonic-gate
6557c478bd9Sstevel@tonic-gate /*
6567c478bd9Sstevel@tonic-gate * If we can't write append the record, seek the file back to
6577c478bd9Sstevel@tonic-gate * the original location and truncate it there in order to make
6587c478bd9Sstevel@tonic-gate * sure the file is always in a sane state w.r.t. libexacct.
6597c478bd9Sstevel@tonic-gate */
6607c478bd9Sstevel@tonic-gate (void) lseek64(lp->log_fd, lp->log_off, SEEK_SET);
6617c478bd9Sstevel@tonic-gate (void) ftruncate64(lp->log_fd, lp->log_off);
6627c478bd9Sstevel@tonic-gate }
6637c478bd9Sstevel@tonic-gate
6647c478bd9Sstevel@tonic-gate (void) pthread_mutex_unlock(&lp->log_lock);
6657c478bd9Sstevel@tonic-gate (void) pthread_mutex_unlock(&ep->ev_lock);
6667c478bd9Sstevel@tonic-gate
6677c478bd9Sstevel@tonic-gate fmd_free(eabuf, easize);
6687ee93e3bSdilpreet
6697ee93e3bSdilpreet exerrcp:
6707ee93e3bSdilpreet if (cp != NULL) {
6717ee93e3bSdilpreet if (fe != NULL) {
6727ee93e3bSdilpreet ea_object_t **fp = fe;
6737ee93e3bSdilpreet int i = 0;
6747ee93e3bSdilpreet
6757ee93e3bSdilpreet for (; *fp != NULL && i < cip->ci_nitems; i++)
6767ee93e3bSdilpreet (void) ea_free_item(*fp++, EUP_ALLOC);
6777ee93e3bSdilpreet fmd_free(fe, frsize);
6787ee93e3bSdilpreet }
6797ee93e3bSdilpreet
6807ee93e3bSdilpreet fmd_free(items, itsize);
6817ee93e3bSdilpreet }
6827ee93e3bSdilpreet
6837c478bd9Sstevel@tonic-gate exerr:
6847c478bd9Sstevel@tonic-gate fmd_free(nvbuf, nvsize);
6857c478bd9Sstevel@tonic-gate
6867c478bd9Sstevel@tonic-gate (void) ea_free_item(&i0, EUP_ALLOC);
6877c478bd9Sstevel@tonic-gate (void) ea_free_item(&i1, EUP_ALLOC);
6887c478bd9Sstevel@tonic-gate (void) ea_free_item(&i2, EUP_ALLOC);
6897c478bd9Sstevel@tonic-gate
6907c478bd9Sstevel@tonic-gate /*
6917c478bd9Sstevel@tonic-gate * Keep track of out-of-space errors using global statistics. As we're
6927c478bd9Sstevel@tonic-gate * out of disk space, it's unlikely the EFMD_LOG_APPEND will be logged.
6937c478bd9Sstevel@tonic-gate */
6947c478bd9Sstevel@tonic-gate if (err == ENOSPC || err == EFMD_LOG_MINFREE) {
6957c478bd9Sstevel@tonic-gate fmd_stat_t *sp;
6967c478bd9Sstevel@tonic-gate
6977c478bd9Sstevel@tonic-gate if (lp == fmd.d_errlog)
6987c478bd9Sstevel@tonic-gate sp = &fmd.d_stats->ds_err_enospc;
6997c478bd9Sstevel@tonic-gate else if (lp == fmd.d_fltlog)
7007c478bd9Sstevel@tonic-gate sp = &fmd.d_stats->ds_flt_enospc;
7017c478bd9Sstevel@tonic-gate else
7027c478bd9Sstevel@tonic-gate sp = &fmd.d_stats->ds_oth_enospc;
7037c478bd9Sstevel@tonic-gate
7047c478bd9Sstevel@tonic-gate (void) pthread_mutex_lock(&fmd.d_stats_lock);
7057c478bd9Sstevel@tonic-gate sp->fmds_value.ui64++;
7067c478bd9Sstevel@tonic-gate (void) pthread_mutex_unlock(&fmd.d_stats_lock);
7077c478bd9Sstevel@tonic-gate }
7087c478bd9Sstevel@tonic-gate
7097c478bd9Sstevel@tonic-gate if (err != 0) {
7107c478bd9Sstevel@tonic-gate fmd_error(EFMD_LOG_APPEND, "failed to log_append %s %p: %s\n",
7117c478bd9Sstevel@tonic-gate lp->log_tag, (void *)ep, fmd_strerror(err));
7127c478bd9Sstevel@tonic-gate }
7137c478bd9Sstevel@tonic-gate }
7147c478bd9Sstevel@tonic-gate
7157c478bd9Sstevel@tonic-gate /*
7167c478bd9Sstevel@tonic-gate * Commit an event to the log permanently, indicating that it should not be
7177c478bd9Sstevel@tonic-gate * replayed on restart. This is done by overwriting the event group's catalog
7187c478bd9Sstevel@tonic-gate * code with EXD_GROUP_FMA (from EXD_GROUP_RFMA used in fmd_log_append()). We
7197c478bd9Sstevel@tonic-gate * use pwrite64() to update the existing word directly, using somewhat guilty
7207c478bd9Sstevel@tonic-gate * knowledge that exacct stores the 32-bit catalog word first for each object.
7217c478bd9Sstevel@tonic-gate * Since we are overwriting an existing log location using pwrite64() and hold
7227c478bd9Sstevel@tonic-gate * the event lock, we do not need to hold the log_lock during the i/o.
7237c478bd9Sstevel@tonic-gate */
7247c478bd9Sstevel@tonic-gate void
fmd_log_commit(fmd_log_t * lp,fmd_event_t * e)7257c478bd9Sstevel@tonic-gate fmd_log_commit(fmd_log_t *lp, fmd_event_t *e)
7267c478bd9Sstevel@tonic-gate {
7277c478bd9Sstevel@tonic-gate fmd_event_impl_t *ep = (fmd_event_impl_t *)e;
7287c478bd9Sstevel@tonic-gate ea_catalog_t c;
7297c478bd9Sstevel@tonic-gate int err = 0;
7307c478bd9Sstevel@tonic-gate
7317c478bd9Sstevel@tonic-gate if (!(lp->log_flags & FMD_LF_REPLAY))
7327c478bd9Sstevel@tonic-gate return; /* log does not require replay tagging */
7337c478bd9Sstevel@tonic-gate
7347c478bd9Sstevel@tonic-gate ASSERT(MUTEX_HELD(&ep->ev_lock));
7357c478bd9Sstevel@tonic-gate ASSERT(ep->ev_log == lp && ep->ev_off != 0);
7367c478bd9Sstevel@tonic-gate
7377c478bd9Sstevel@tonic-gate c = CAT_FMA_GROUP;
7387c478bd9Sstevel@tonic-gate exacct_order32(&c);
7397c478bd9Sstevel@tonic-gate
7407c478bd9Sstevel@tonic-gate if (pwrite64(lp->log_fd, &c, sizeof (c), ep->ev_off) == sizeof (c)) {
7417c478bd9Sstevel@tonic-gate TRACE((FMD_DBG_LOG, "commit %s %p", lp->log_tag, (void *)ep));
7427c478bd9Sstevel@tonic-gate ep->ev_flags &= ~FMD_EVF_REPLAY;
7437c478bd9Sstevel@tonic-gate
7447c478bd9Sstevel@tonic-gate /*
7457c478bd9Sstevel@tonic-gate * If we have committed the event, check to see if the TOC skip
7467c478bd9Sstevel@tonic-gate * offset needs to be updated, and decrement the pending count.
7477c478bd9Sstevel@tonic-gate */
7487c478bd9Sstevel@tonic-gate (void) pthread_mutex_lock(&lp->log_lock);
7497c478bd9Sstevel@tonic-gate
7507c478bd9Sstevel@tonic-gate if (lp->log_skip == ep->ev_off) {
7517c478bd9Sstevel@tonic-gate lp->log_flags |= FMD_LF_DIRTY;
7527c478bd9Sstevel@tonic-gate lp->log_skip += ep->ev_len;
7537c478bd9Sstevel@tonic-gate }
7547c478bd9Sstevel@tonic-gate
7557c478bd9Sstevel@tonic-gate ASSERT(lp->log_pending != 0);
7567c478bd9Sstevel@tonic-gate lp->log_pending--;
7577c478bd9Sstevel@tonic-gate
7587c478bd9Sstevel@tonic-gate (void) pthread_cond_broadcast(&lp->log_cv);
759d9638e54Smws (void) pthread_mutex_unlock(&lp->log_lock);
7607c478bd9Sstevel@tonic-gate
7617c478bd9Sstevel@tonic-gate } else {
7627c478bd9Sstevel@tonic-gate fmd_error(EFMD_LOG_COMMIT, "failed to log_commit %s %p: %s\n",
7637c478bd9Sstevel@tonic-gate lp->log_tag, (void *)ep, fmd_strerror(err));
7647c478bd9Sstevel@tonic-gate }
7657c478bd9Sstevel@tonic-gate }
7667c478bd9Sstevel@tonic-gate
7677c478bd9Sstevel@tonic-gate /*
7687c478bd9Sstevel@tonic-gate * If we need to destroy an event and it wasn't able to be committed, we permit
7697c478bd9Sstevel@tonic-gate * the owner to decommit from ever trying again. This operation decrements the
7707c478bd9Sstevel@tonic-gate * pending count on the log and broadcasts to anyone waiting on log_cv.
7717c478bd9Sstevel@tonic-gate */
7727c478bd9Sstevel@tonic-gate void
fmd_log_decommit(fmd_log_t * lp,fmd_event_t * e)7737c478bd9Sstevel@tonic-gate fmd_log_decommit(fmd_log_t *lp, fmd_event_t *e)
7747c478bd9Sstevel@tonic-gate {
7757c478bd9Sstevel@tonic-gate fmd_event_impl_t *ep = (fmd_event_impl_t *)e;
7767c478bd9Sstevel@tonic-gate
7777c478bd9Sstevel@tonic-gate if (!(lp->log_flags & FMD_LF_REPLAY))
7787c478bd9Sstevel@tonic-gate return; /* log does not require replay tagging */
7797c478bd9Sstevel@tonic-gate
7807c478bd9Sstevel@tonic-gate ASSERT(MUTEX_HELD(&ep->ev_lock));
7817c478bd9Sstevel@tonic-gate ASSERT(ep->ev_log == lp);
7827c478bd9Sstevel@tonic-gate
7837c478bd9Sstevel@tonic-gate (void) pthread_mutex_lock(&lp->log_lock);
7847c478bd9Sstevel@tonic-gate
7857c478bd9Sstevel@tonic-gate TRACE((FMD_DBG_LOG, "decommit %s %p", lp->log_tag, (void *)ep));
7867c478bd9Sstevel@tonic-gate ep->ev_flags &= ~FMD_EVF_REPLAY;
7877c478bd9Sstevel@tonic-gate
7887c478bd9Sstevel@tonic-gate ASSERT(lp->log_pending != 0);
7897c478bd9Sstevel@tonic-gate lp->log_pending--;
7907c478bd9Sstevel@tonic-gate
7917c478bd9Sstevel@tonic-gate (void) pthread_cond_broadcast(&lp->log_cv);
792d9638e54Smws (void) pthread_mutex_unlock(&lp->log_lock);
7937c478bd9Sstevel@tonic-gate }
7947c478bd9Sstevel@tonic-gate
7957c478bd9Sstevel@tonic-gate static fmd_event_t *
fmd_log_unpack(fmd_log_t * lp,ea_object_t * grp,off64_t off)7967c478bd9Sstevel@tonic-gate fmd_log_unpack(fmd_log_t *lp, ea_object_t *grp, off64_t off)
7977c478bd9Sstevel@tonic-gate {
7987c478bd9Sstevel@tonic-gate fmd_timeval_t ftv = { -1ULL, -1ULL };
7997c478bd9Sstevel@tonic-gate nvlist_t *nvl = NULL;
8007c478bd9Sstevel@tonic-gate
8017c478bd9Sstevel@tonic-gate ea_object_t *obj;
8027c478bd9Sstevel@tonic-gate char *class;
8037c478bd9Sstevel@tonic-gate int err;
8047c478bd9Sstevel@tonic-gate
8057c478bd9Sstevel@tonic-gate for (obj = grp->eo_group.eg_objs; obj != NULL; obj = obj->eo_next) {
8067c478bd9Sstevel@tonic-gate switch (obj->eo_catalog) {
8077c478bd9Sstevel@tonic-gate case CAT_FMA_NVLIST:
8087c478bd9Sstevel@tonic-gate if ((err = nvlist_xunpack(obj->eo_item.ei_raw,
8097c478bd9Sstevel@tonic-gate obj->eo_item.ei_size, &nvl, &fmd.d_nva)) != 0) {
8107c478bd9Sstevel@tonic-gate fmd_error(EFMD_LOG_UNPACK, "failed to unpack "
8117c478bd9Sstevel@tonic-gate "log nvpair: %s\n", fmd_strerror(err));
8127c478bd9Sstevel@tonic-gate return (NULL);
8137c478bd9Sstevel@tonic-gate }
8147c478bd9Sstevel@tonic-gate break;
8157c478bd9Sstevel@tonic-gate
8167c478bd9Sstevel@tonic-gate case CAT_FMA_TODSEC:
8177c478bd9Sstevel@tonic-gate ftv.ftv_sec = obj->eo_item.ei_uint64;
8187c478bd9Sstevel@tonic-gate break;
8197c478bd9Sstevel@tonic-gate
8207c478bd9Sstevel@tonic-gate case CAT_FMA_TODNSEC:
8217c478bd9Sstevel@tonic-gate ftv.ftv_nsec = obj->eo_item.ei_uint64;
8227c478bd9Sstevel@tonic-gate break;
8237c478bd9Sstevel@tonic-gate }
8247c478bd9Sstevel@tonic-gate }
8257c478bd9Sstevel@tonic-gate
8267c478bd9Sstevel@tonic-gate if (nvl == NULL || ftv.ftv_sec == -1ULL || ftv.ftv_nsec == -1ULL) {
8277c478bd9Sstevel@tonic-gate fmd_error(EFMD_LOG_UNPACK, "failed to unpack log event: "
8287c478bd9Sstevel@tonic-gate "required object(s) missing from record group\n");
8297c478bd9Sstevel@tonic-gate nvlist_free(nvl);
8307c478bd9Sstevel@tonic-gate return (NULL);
8317c478bd9Sstevel@tonic-gate }
8327c478bd9Sstevel@tonic-gate
8337c478bd9Sstevel@tonic-gate if (nvlist_lookup_string(nvl, FM_CLASS, &class) != 0) {
8347c478bd9Sstevel@tonic-gate fmd_error(EFMD_LOG_UNPACK, "failed to unpack log event: "
8357c478bd9Sstevel@tonic-gate "record is missing required '%s' nvpair\n", FM_CLASS);
8367c478bd9Sstevel@tonic-gate nvlist_free(nvl);
8377c478bd9Sstevel@tonic-gate return (NULL);
8387c478bd9Sstevel@tonic-gate }
8397c478bd9Sstevel@tonic-gate
8407c478bd9Sstevel@tonic-gate return (fmd_event_recreate(FMD_EVT_PROTOCOL,
8417c478bd9Sstevel@tonic-gate &ftv, nvl, class, lp, off, ea_pack_object(grp, NULL, 0)));
8427c478bd9Sstevel@tonic-gate }
8437c478bd9Sstevel@tonic-gate
8447c478bd9Sstevel@tonic-gate /*
8457c478bd9Sstevel@tonic-gate * Replay event(s) from the specified log by invoking the specified callback
8467c478bd9Sstevel@tonic-gate * function 'func' for each event. If the log has the FMD_LF_REPLAY flag set,
8477c478bd9Sstevel@tonic-gate * we replay all events after log_skip that have the FMA_RGROUP group tag.
8487c478bd9Sstevel@tonic-gate * This mode is used for the error telemetry log. If the log does not have
8497c478bd9Sstevel@tonic-gate * this flag set (used for ASRU logs), only the most recent event is replayed.
8507c478bd9Sstevel@tonic-gate */
8517c478bd9Sstevel@tonic-gate void
fmd_log_replay(fmd_log_t * lp,fmd_log_f * func,void * data)8527c478bd9Sstevel@tonic-gate fmd_log_replay(fmd_log_t *lp, fmd_log_f *func, void *data)
8537c478bd9Sstevel@tonic-gate {
8547c478bd9Sstevel@tonic-gate ea_object_t obj, *grp;
8557c478bd9Sstevel@tonic-gate ea_object_type_t type;
8567c478bd9Sstevel@tonic-gate ea_catalog_t c;
8577c478bd9Sstevel@tonic-gate fmd_event_t *ep;
8587c478bd9Sstevel@tonic-gate off64_t off, skp;
8597c478bd9Sstevel@tonic-gate uint_t n = 0;
8607c478bd9Sstevel@tonic-gate
8617c478bd9Sstevel@tonic-gate (void) pthread_mutex_lock(&lp->log_lock);
8627c478bd9Sstevel@tonic-gate
8637c478bd9Sstevel@tonic-gate if (lp->log_stat.st_size == 0 && (lp->log_flags & FMD_LF_REPLAY)) {
8647c478bd9Sstevel@tonic-gate (void) pthread_mutex_unlock(&lp->log_lock);
8657c478bd9Sstevel@tonic-gate return; /* we just created this log: never replay events */
8667c478bd9Sstevel@tonic-gate }
8677c478bd9Sstevel@tonic-gate
8687c478bd9Sstevel@tonic-gate while (lp->log_flags & FMD_LF_BUSY)
8697c478bd9Sstevel@tonic-gate (void) pthread_cond_wait(&lp->log_cv, &lp->log_lock);
8707c478bd9Sstevel@tonic-gate
8717c478bd9Sstevel@tonic-gate if (lp->log_off == lp->log_beg) {
8727c478bd9Sstevel@tonic-gate (void) pthread_mutex_unlock(&lp->log_lock);
8737c478bd9Sstevel@tonic-gate return; /* no records appended yet */
8747c478bd9Sstevel@tonic-gate }
8757c478bd9Sstevel@tonic-gate
8767c478bd9Sstevel@tonic-gate lp->log_flags |= FMD_LF_BUSY;
8777c478bd9Sstevel@tonic-gate skp = lp->log_skip;
8787c478bd9Sstevel@tonic-gate ea_clear(&lp->log_ea); /* resync exacct file */
8797c478bd9Sstevel@tonic-gate
8807c478bd9Sstevel@tonic-gate /*
8817c478bd9Sstevel@tonic-gate * If FMD_LF_REPLAY is set, begin our replay at either log_skip (if it
8827c478bd9Sstevel@tonic-gate * is non-zero) or at log_beg. Otherwise replay from the end (log_off)
8837c478bd9Sstevel@tonic-gate */
8847c478bd9Sstevel@tonic-gate if (lp->log_flags & FMD_LF_REPLAY) {
8857c478bd9Sstevel@tonic-gate off = MAX(lp->log_beg, lp->log_skip);
8867c478bd9Sstevel@tonic-gate c = CAT_FMA_RGROUP;
8877c478bd9Sstevel@tonic-gate } else {
8887c478bd9Sstevel@tonic-gate off = lp->log_off;
8897c478bd9Sstevel@tonic-gate c = CAT_FMA_GROUP;
8907c478bd9Sstevel@tonic-gate }
8917c478bd9Sstevel@tonic-gate
8927c478bd9Sstevel@tonic-gate if (lseek64(lp->log_fd, off, SEEK_SET) != off) {
8937c478bd9Sstevel@tonic-gate fmd_panic("failed to seek %s to 0x%llx\n",
8947c478bd9Sstevel@tonic-gate lp->log_name, (u_longlong_t)off);
8957c478bd9Sstevel@tonic-gate }
8967c478bd9Sstevel@tonic-gate
8977c478bd9Sstevel@tonic-gate /*
8987c478bd9Sstevel@tonic-gate * If FMD_LF_REPLAY is not set, back up to the start of the previous
8997c478bd9Sstevel@tonic-gate * object and make sure this object is an EO_GROUP; otherwise return.
9007c478bd9Sstevel@tonic-gate */
9017c478bd9Sstevel@tonic-gate if (!(lp->log_flags & FMD_LF_REPLAY) &&
9027c478bd9Sstevel@tonic-gate (type = ea_previous_object(&lp->log_ea, &obj)) != EO_GROUP) {
9037c478bd9Sstevel@tonic-gate fmd_error(EFMD_LOG_REPLAY, "last log object is of unexpected "
9047c478bd9Sstevel@tonic-gate "type %d (log may be truncated or corrupt)\n", type);
9057c478bd9Sstevel@tonic-gate goto out;
9067c478bd9Sstevel@tonic-gate }
9077c478bd9Sstevel@tonic-gate
9087c478bd9Sstevel@tonic-gate while ((grp = ea_get_object_tree(&lp->log_ea, 1)) != NULL) {
9097c478bd9Sstevel@tonic-gate if (!(lp->log_flags & FMD_LF_REPLAY))
9107c478bd9Sstevel@tonic-gate off -= ea_pack_object(grp, NULL, 0);
9117c478bd9Sstevel@tonic-gate else if (n == 0 && grp->eo_catalog == CAT_FMA_GROUP)
9127c478bd9Sstevel@tonic-gate skp = off; /* update skip */
9137c478bd9Sstevel@tonic-gate
9147c478bd9Sstevel@tonic-gate /*
9157c478bd9Sstevel@tonic-gate * We temporarily drop log_lock around the call to unpack the
9167c478bd9Sstevel@tonic-gate * event, hold it, and perform the callback, because these
9177c478bd9Sstevel@tonic-gate * operations may try to acquire log_lock to bump log_refs.
9187c478bd9Sstevel@tonic-gate * We cannot lose control because the FMD_LF_BUSY flag is set.
9197c478bd9Sstevel@tonic-gate */
9207c478bd9Sstevel@tonic-gate (void) pthread_mutex_unlock(&lp->log_lock);
9217c478bd9Sstevel@tonic-gate
9227c478bd9Sstevel@tonic-gate if (grp->eo_catalog == c &&
9237c478bd9Sstevel@tonic-gate (ep = fmd_log_unpack(lp, grp, off)) != NULL) {
9247c478bd9Sstevel@tonic-gate
9257c478bd9Sstevel@tonic-gate TRACE((FMD_DBG_LOG, "replay %s %p off %llx",
9267c478bd9Sstevel@tonic-gate lp->log_tag, (void *)ep, (u_longlong_t)off));
9277c478bd9Sstevel@tonic-gate
9287c478bd9Sstevel@tonic-gate fmd_event_hold(ep);
9297c478bd9Sstevel@tonic-gate func(lp, ep, data);
9307c478bd9Sstevel@tonic-gate fmd_event_rele(ep);
9317c478bd9Sstevel@tonic-gate n++;
9327c478bd9Sstevel@tonic-gate }
9337c478bd9Sstevel@tonic-gate
9347c478bd9Sstevel@tonic-gate (void) pthread_mutex_lock(&lp->log_lock);
9357c478bd9Sstevel@tonic-gate off += ea_pack_object(grp, NULL, 0);
9367c478bd9Sstevel@tonic-gate ea_free_object(grp, EUP_ALLOC);
9377c478bd9Sstevel@tonic-gate }
9387c478bd9Sstevel@tonic-gate
9397c478bd9Sstevel@tonic-gate if (ea_error() != EXR_EOF) {
9407c478bd9Sstevel@tonic-gate fmd_error(EFMD_LOG_REPLAY, "failed to replay %s event at "
9417c478bd9Sstevel@tonic-gate "offset 0x%llx: %s\n", lp->log_name, (u_longlong_t)off,
9427c478bd9Sstevel@tonic-gate fmd_ea_strerror(ea_error()));
9437c478bd9Sstevel@tonic-gate }
9447c478bd9Sstevel@tonic-gate
9457c478bd9Sstevel@tonic-gate if (n == 0)
9467c478bd9Sstevel@tonic-gate skp = off; /* if no replays, move skip to where we ended up */
9477c478bd9Sstevel@tonic-gate
9487c478bd9Sstevel@tonic-gate out:
9497c478bd9Sstevel@tonic-gate if (lseek64(lp->log_fd, lp->log_off, SEEK_SET) != lp->log_off) {
9507c478bd9Sstevel@tonic-gate fmd_panic("failed to seek %s to 0x%llx\n",
9517c478bd9Sstevel@tonic-gate lp->log_name, (u_longlong_t)lp->log_off);
9527c478bd9Sstevel@tonic-gate }
9537c478bd9Sstevel@tonic-gate
9547c478bd9Sstevel@tonic-gate if (skp != lp->log_skip) {
9557c478bd9Sstevel@tonic-gate lp->log_flags |= FMD_LF_DIRTY;
9567c478bd9Sstevel@tonic-gate lp->log_skip = skp;
9577c478bd9Sstevel@tonic-gate }
9587c478bd9Sstevel@tonic-gate
9597c478bd9Sstevel@tonic-gate lp->log_flags &= ~FMD_LF_BUSY;
9607c478bd9Sstevel@tonic-gate (void) pthread_cond_broadcast(&lp->log_cv);
961d9638e54Smws (void) pthread_mutex_unlock(&lp->log_lock);
9627c478bd9Sstevel@tonic-gate }
9637c478bd9Sstevel@tonic-gate
9647c478bd9Sstevel@tonic-gate void
fmd_log_update(fmd_log_t * lp)9657c478bd9Sstevel@tonic-gate fmd_log_update(fmd_log_t *lp)
9667c478bd9Sstevel@tonic-gate {
9677c478bd9Sstevel@tonic-gate ea_object_t toc, item;
9687c478bd9Sstevel@tonic-gate off64_t skip = 0;
9697c478bd9Sstevel@tonic-gate size_t size;
9707c478bd9Sstevel@tonic-gate void *buf;
9717c478bd9Sstevel@tonic-gate
9727c478bd9Sstevel@tonic-gate (void) pthread_mutex_lock(&lp->log_lock);
9737c478bd9Sstevel@tonic-gate
9747c478bd9Sstevel@tonic-gate if (lp->log_flags & FMD_LF_DIRTY) {
9757c478bd9Sstevel@tonic-gate lp->log_flags &= ~FMD_LF_DIRTY;
9767c478bd9Sstevel@tonic-gate skip = lp->log_skip;
9777c478bd9Sstevel@tonic-gate }
9787c478bd9Sstevel@tonic-gate
9797c478bd9Sstevel@tonic-gate (void) pthread_mutex_unlock(&lp->log_lock);
9807c478bd9Sstevel@tonic-gate
9817c478bd9Sstevel@tonic-gate /*
9827c478bd9Sstevel@tonic-gate * If the skip needs to be updated, construct a TOC record group
9837c478bd9Sstevel@tonic-gate * containing the skip offset and overwrite the TOC in-place.
9847c478bd9Sstevel@tonic-gate */
9857c478bd9Sstevel@tonic-gate if (skip != 0 && ea_set_group(&toc, CAT_FMA_GROUP) == 0 &&
9867c478bd9Sstevel@tonic-gate ea_set_item(&item, CAT_FMA_OFFSET, &skip, 0) == 0) {
9877c478bd9Sstevel@tonic-gate
9887c478bd9Sstevel@tonic-gate (void) ea_attach_to_group(&toc, &item);
9897c478bd9Sstevel@tonic-gate size = ea_pack_object(&toc, NULL, 0);
9907c478bd9Sstevel@tonic-gate buf = fmd_alloc(size, FMD_SLEEP);
9917c478bd9Sstevel@tonic-gate
9927c478bd9Sstevel@tonic-gate (void) ea_pack_object(&toc, buf, size);
9937c478bd9Sstevel@tonic-gate ASSERT(lp->log_toc + size == lp->log_beg);
9947c478bd9Sstevel@tonic-gate
9957c478bd9Sstevel@tonic-gate if (pwrite64(lp->log_fd, buf, size, lp->log_toc) == size) {
9967c478bd9Sstevel@tonic-gate TRACE((FMD_DBG_LOG, "updated skip to %llx", skip));
9977c478bd9Sstevel@tonic-gate } else {
9987c478bd9Sstevel@tonic-gate fmd_error(EFMD_LOG_UPDATE,
9997c478bd9Sstevel@tonic-gate "failed to log_update %s", lp->log_tag);
10007c478bd9Sstevel@tonic-gate }
10017c478bd9Sstevel@tonic-gate
10027c478bd9Sstevel@tonic-gate fmd_free(buf, size);
10037c478bd9Sstevel@tonic-gate (void) ea_free_item(&item, EUP_ALLOC);
10047c478bd9Sstevel@tonic-gate }
10057c478bd9Sstevel@tonic-gate }
10067c478bd9Sstevel@tonic-gate
10077c478bd9Sstevel@tonic-gate /*
10087c478bd9Sstevel@tonic-gate * Rotate the specified log by renaming its underlying file to a staging file
10097c478bd9Sstevel@tonic-gate * that can be handed off to logadm(1M) or an administrator script. If the
10107c478bd9Sstevel@tonic-gate * rename succeeds, open a new log file using the old path and return it.
10117c478bd9Sstevel@tonic-gate * Note that we are relying our caller to use some higher-level mechanism to
10127c478bd9Sstevel@tonic-gate * ensure that fmd_log_rotate() cannot be called while other threads are
10137c478bd9Sstevel@tonic-gate * attempting fmd_log_append() using the same log (fmd's d_log_lock is used
10147c478bd9Sstevel@tonic-gate * for the global errlog and fltlog).
10157c478bd9Sstevel@tonic-gate */
10167c478bd9Sstevel@tonic-gate fmd_log_t *
fmd_log_rotate(fmd_log_t * lp)10177c478bd9Sstevel@tonic-gate fmd_log_rotate(fmd_log_t *lp)
10187c478bd9Sstevel@tonic-gate {
10197c478bd9Sstevel@tonic-gate char npath[PATH_MAX];
10207c478bd9Sstevel@tonic-gate fmd_log_t *nlp;
10217c478bd9Sstevel@tonic-gate
1022*9dd0f810Scindi (void) snprintf(npath, sizeof (npath), "%s+", lp->log_name);
1023*9dd0f810Scindi
1024*9dd0f810Scindi /*
1025*9dd0f810Scindi * Open new log file.
1026*9dd0f810Scindi */
1027*9dd0f810Scindi if ((nlp = fmd_log_open("", npath, lp->log_tag)) == NULL) {
1028*9dd0f810Scindi fmd_error(EFMD_LOG_ROTATE, "failed to open %s", npath);
1029*9dd0f810Scindi (void) fmd_set_errno(EFMD_LOG_ROTATE);
1030*9dd0f810Scindi return (NULL);
1031*9dd0f810Scindi }
1032*9dd0f810Scindi
10337c478bd9Sstevel@tonic-gate (void) snprintf(npath, sizeof (npath), "%s.0-", lp->log_name);
10347c478bd9Sstevel@tonic-gate (void) pthread_mutex_lock(&lp->log_lock);
10357c478bd9Sstevel@tonic-gate
10367c478bd9Sstevel@tonic-gate /*
10377c478bd9Sstevel@tonic-gate * Check for any pending commits to drain before proceeding. We can't
10387c478bd9Sstevel@tonic-gate * rotate the log out if commits are pending because if we die after
10397c478bd9Sstevel@tonic-gate * the log is moved aside, we won't be able to replay them on restart.
10407c478bd9Sstevel@tonic-gate */
10417c478bd9Sstevel@tonic-gate if (lp->log_pending != 0) {
10427c478bd9Sstevel@tonic-gate (void) pthread_mutex_unlock(&lp->log_lock);
1043*9dd0f810Scindi (void) unlink(nlp->log_name);
1044*9dd0f810Scindi fmd_log_rele(nlp);
10457c478bd9Sstevel@tonic-gate (void) fmd_set_errno(EFMD_LOG_ROTBUSY);
10467c478bd9Sstevel@tonic-gate return (NULL);
10477c478bd9Sstevel@tonic-gate }
10487c478bd9Sstevel@tonic-gate
10497c478bd9Sstevel@tonic-gate if (rename(lp->log_name, npath) != 0) {
10507c478bd9Sstevel@tonic-gate (void) pthread_mutex_unlock(&lp->log_lock);
10517c478bd9Sstevel@tonic-gate fmd_error(EFMD_LOG_ROTATE, "failed to rename %s", lp->log_name);
1052*9dd0f810Scindi (void) unlink(nlp->log_name);
1053*9dd0f810Scindi fmd_log_rele(nlp);
10547c478bd9Sstevel@tonic-gate (void) fmd_set_errno(EFMD_LOG_ROTATE);
10557c478bd9Sstevel@tonic-gate return (NULL);
10567c478bd9Sstevel@tonic-gate }
10577c478bd9Sstevel@tonic-gate
1058*9dd0f810Scindi if (rename(nlp->log_name, lp->log_name) != 0) {
10597c478bd9Sstevel@tonic-gate (void) pthread_mutex_unlock(&lp->log_lock);
1060*9dd0f810Scindi fmd_error(EFMD_LOG_ROTATE, "failed to rename %s",
1061*9dd0f810Scindi nlp->log_name);
1062*9dd0f810Scindi (void) unlink(nlp->log_name);
1063*9dd0f810Scindi fmd_log_rele(nlp);
10647c478bd9Sstevel@tonic-gate (void) fmd_set_errno(EFMD_LOG_ROTATE);
10657c478bd9Sstevel@tonic-gate return (NULL);
10667c478bd9Sstevel@tonic-gate }
10677c478bd9Sstevel@tonic-gate
10687c478bd9Sstevel@tonic-gate /*
1069*9dd0f810Scindi * Change name of new log file
1070*9dd0f810Scindi */
1071*9dd0f810Scindi fmd_strfree(nlp->log_name);
1072*9dd0f810Scindi nlp->log_name = fmd_strdup(lp->log_name, FMD_SLEEP);
1073*9dd0f810Scindi
1074*9dd0f810Scindi /*
10757c478bd9Sstevel@tonic-gate * If we've rotated the log, no pending events exist so we don't have
10767c478bd9Sstevel@tonic-gate * any more commits coming, and our caller should have arranged for
10777c478bd9Sstevel@tonic-gate * no more calls to append. As such, we can close log_fd for good.
10787c478bd9Sstevel@tonic-gate */
10797c478bd9Sstevel@tonic-gate if (lp->log_flags & FMD_LF_EAOPEN) {
10807c478bd9Sstevel@tonic-gate (void) ea_close(&lp->log_ea);
10817c478bd9Sstevel@tonic-gate lp->log_flags &= ~FMD_LF_EAOPEN;
10827c478bd9Sstevel@tonic-gate }
10837c478bd9Sstevel@tonic-gate
10847c478bd9Sstevel@tonic-gate (void) close(lp->log_fd);
10857c478bd9Sstevel@tonic-gate lp->log_fd = -1;
10867c478bd9Sstevel@tonic-gate
10877c478bd9Sstevel@tonic-gate (void) pthread_mutex_unlock(&lp->log_lock);
10887c478bd9Sstevel@tonic-gate return (nlp);
10897c478bd9Sstevel@tonic-gate }
1090