xref: /onnv-gate/usr/src/cmd/fm/fmd/common/fmd_log.c (revision 3323:3cff27a475a5)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*3323Scindi  * Common Development and Distribution License (the "License").
6*3323Scindi  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
211193Smws 
220Sstevel@tonic-gate /*
23*3323Scindi  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
240Sstevel@tonic-gate  * Use is subject to license terms.
250Sstevel@tonic-gate  */
260Sstevel@tonic-gate 
270Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
280Sstevel@tonic-gate 
290Sstevel@tonic-gate /*
300Sstevel@tonic-gate  * FMD Log File Subsystem
310Sstevel@tonic-gate  *
320Sstevel@tonic-gate  * Events are written to one of two log files as they are received or created;
330Sstevel@tonic-gate  * the error log tracks all ereport.* events received on the inbound event
340Sstevel@tonic-gate  * transport, and the fault log tracks all list.* events generated by fmd or
350Sstevel@tonic-gate  * its client modules.  In addition, we use the same log file format to cache
360Sstevel@tonic-gate  * state and events associated with ASRUs that are named in a diagnosis.
370Sstevel@tonic-gate  *
380Sstevel@tonic-gate  * The log files use the exacct format manipulated by libexacct(3LIB) and
390Sstevel@tonic-gate  * originally defined in PSARC 1999/119.  However, the exacct library was
400Sstevel@tonic-gate  * designed primarily for read-only clients and without the synchronous i/o
410Sstevel@tonic-gate  * considerations and seeking required for fmd, so we use libexacct here only
420Sstevel@tonic-gate  * to read and write the file headers and to pack data from memory into a file
430Sstevel@tonic-gate  * bytestream.  All of the i/o and file offset manipulations are performed by
440Sstevel@tonic-gate  * the fmd code below.  Our exacct file management uses the following grammar:
450Sstevel@tonic-gate  *
460Sstevel@tonic-gate  * file := hdr toc event*
471052Sdilpreet  * hdr := EXD_FMA_LABEL EXD_FMA_VERSION EXD_FMA_OSREL EXD_FMA_OSVER
481052Sdilpreet  * EXD_FMA_PLAT EXD_FMA_UUID
490Sstevel@tonic-gate  * toc := EXD_FMA_OFFSET
501052Sdilpreet  * event := EXD_FMA_TODSEC EXD_FMA_TODNSEC EXD_FMA_NVLIST evref* or legacy evref
511052Sdilpreet  * evref := EXD_FMA_UUID EXD_FMA_OFFSET
521052Sdilpreet  * legacy evref := EXD_FMA_MAJOR EXD_FMA_MINOR EXD_FMA_INODE EXD_FMA_OFFSET
530Sstevel@tonic-gate  *
540Sstevel@tonic-gate  * Any event can be uniquely identified by the tuple (file, offset) where file
551052Sdilpreet  * is encoded as (uuid) when we are cross-linking files.  For legacy file
561052Sdilpreet  * formats we still support encoding the reference as (major, minor, inode).
571052Sdilpreet  * Note that we break out of the file's dev_t into its two 32-bit components to
580Sstevel@tonic-gate  * permit development of either 32-bit or 64-bit log readers and writers; the
590Sstevel@tonic-gate  * LFS APIs do not yet export a 64-bit dev_t to fstat64(), so there is no way
600Sstevel@tonic-gate  * for a 32-bit application to retrieve and store a 64-bit dev_t.
610Sstevel@tonic-gate  *
620Sstevel@tonic-gate  * In order to replay events in the event of an fmd crash, events are initially
630Sstevel@tonic-gate  * written to the error log using the group catalog tag EXD_GROUP_RFMA by the
640Sstevel@tonic-gate  * fmd_log_append() function.  Later, once an event transitions from the
650Sstevel@tonic-gate  * received state to one of its other states (see fmd_event.c for details),
660Sstevel@tonic-gate  * fmd_log_commit() is used to overwrite the tag with EXD_GROUP_FMA, indicating
670Sstevel@tonic-gate  * that the event is fully processed and no longer needs to be replayed.
680Sstevel@tonic-gate  */
690Sstevel@tonic-gate 
700Sstevel@tonic-gate #include <sys/types.h>
710Sstevel@tonic-gate #include <sys/mkdev.h>
720Sstevel@tonic-gate #include <sys/statvfs.h>
730Sstevel@tonic-gate #include <sys/fm/protocol.h>
740Sstevel@tonic-gate #include <sys/exacct_impl.h>
751052Sdilpreet #include <uuid/uuid.h>
760Sstevel@tonic-gate 
770Sstevel@tonic-gate #include <unistd.h>
780Sstevel@tonic-gate #include <limits.h>
790Sstevel@tonic-gate #include <fcntl.h>
800Sstevel@tonic-gate #include <ctype.h>
810Sstevel@tonic-gate 
820Sstevel@tonic-gate #include <fmd_alloc.h>
830Sstevel@tonic-gate #include <fmd_error.h>
840Sstevel@tonic-gate #include <fmd_string.h>
850Sstevel@tonic-gate #include <fmd_event.h>
860Sstevel@tonic-gate #include <fmd_conf.h>
870Sstevel@tonic-gate #include <fmd_subr.h>
880Sstevel@tonic-gate #include <fmd_case.h>
890Sstevel@tonic-gate #include <fmd_log.h>
900Sstevel@tonic-gate 
910Sstevel@tonic-gate #include <fmd.h>
920Sstevel@tonic-gate 
930Sstevel@tonic-gate #define	CAT_FMA_RGROUP	(EXT_GROUP | EXC_DEFAULT | EXD_GROUP_RFMA)
940Sstevel@tonic-gate #define	CAT_FMA_GROUP	(EXT_GROUP | EXC_DEFAULT | EXD_GROUP_FMA)
950Sstevel@tonic-gate 
960Sstevel@tonic-gate #define	CAT_FMA_LABEL	(EXT_STRING | EXC_DEFAULT | EXD_FMA_LABEL)
970Sstevel@tonic-gate #define	CAT_FMA_VERSION	(EXT_STRING | EXC_DEFAULT | EXD_FMA_VERSION)
980Sstevel@tonic-gate #define	CAT_FMA_OSREL	(EXT_STRING | EXC_DEFAULT | EXD_FMA_OSREL)
990Sstevel@tonic-gate #define	CAT_FMA_OSVER	(EXT_STRING | EXC_DEFAULT | EXD_FMA_OSVER)
1000Sstevel@tonic-gate #define	CAT_FMA_PLAT	(EXT_STRING | EXC_DEFAULT | EXD_FMA_PLAT)
1011052Sdilpreet #define	CAT_FMA_UUID	(EXT_STRING | EXC_DEFAULT | EXD_FMA_UUID)
1020Sstevel@tonic-gate #define	CAT_FMA_TODSEC	(EXT_UINT64 | EXC_DEFAULT | EXD_FMA_TODSEC)
1030Sstevel@tonic-gate #define	CAT_FMA_TODNSEC	(EXT_UINT64 | EXC_DEFAULT | EXD_FMA_TODNSEC)
1040Sstevel@tonic-gate #define	CAT_FMA_NVLIST	(EXT_RAW | EXC_DEFAULT | EXD_FMA_NVLIST)
1050Sstevel@tonic-gate #define	CAT_FMA_MAJOR	(EXT_UINT32 | EXC_DEFAULT | EXD_FMA_MAJOR)
1060Sstevel@tonic-gate #define	CAT_FMA_MINOR	(EXT_UINT32 | EXC_DEFAULT | EXD_FMA_MINOR)
1070Sstevel@tonic-gate #define	CAT_FMA_INODE	(EXT_UINT64 | EXC_DEFAULT | EXD_FMA_INODE)
1080Sstevel@tonic-gate #define	CAT_FMA_OFFSET	(EXT_UINT64 | EXC_DEFAULT | EXD_FMA_OFFSET)
1090Sstevel@tonic-gate 
1100Sstevel@tonic-gate static ssize_t
fmd_log_write(fmd_log_t * lp,const void * buf,size_t n)1110Sstevel@tonic-gate fmd_log_write(fmd_log_t *lp, const void *buf, size_t n)
1120Sstevel@tonic-gate {
1130Sstevel@tonic-gate 	ssize_t resid = n;
1140Sstevel@tonic-gate 	ssize_t len;
1150Sstevel@tonic-gate 
1160Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&lp->log_lock));
1170Sstevel@tonic-gate 
1180Sstevel@tonic-gate 	while (resid != 0) {
1190Sstevel@tonic-gate 		if ((len = write(lp->log_fd, buf, resid)) <= 0)
1200Sstevel@tonic-gate 			break;
1210Sstevel@tonic-gate 
1220Sstevel@tonic-gate 		resid -= len;
1230Sstevel@tonic-gate 		buf = (char *)buf + len;
1240Sstevel@tonic-gate 	}
1250Sstevel@tonic-gate 
1260Sstevel@tonic-gate 	if (resid == n && n != 0)
1270Sstevel@tonic-gate 		return (-1);
1280Sstevel@tonic-gate 
1290Sstevel@tonic-gate 	return (n - resid);
1300Sstevel@tonic-gate }
1310Sstevel@tonic-gate 
1320Sstevel@tonic-gate static int
fmd_log_write_hdr(fmd_log_t * lp,const char * tag)1330Sstevel@tonic-gate fmd_log_write_hdr(fmd_log_t *lp, const char *tag)
1340Sstevel@tonic-gate {
1351052Sdilpreet 	ea_object_t hdr, toc, i0, i1, i2, i3, i4, i5, i6;
1360Sstevel@tonic-gate 	const char *osrel, *osver, *plat;
1370Sstevel@tonic-gate 	off64_t off = 0;
1380Sstevel@tonic-gate 	int err = 0;
1391052Sdilpreet 	uuid_t uuid;
1400Sstevel@tonic-gate 
1410Sstevel@tonic-gate 	(void) fmd_conf_getprop(fmd.d_conf, "osrelease", &osrel);
1420Sstevel@tonic-gate 	(void) fmd_conf_getprop(fmd.d_conf, "osversion", &osver);
1430Sstevel@tonic-gate 	(void) fmd_conf_getprop(fmd.d_conf, "platform", &plat);
1441052Sdilpreet 	(void) fmd_conf_getprop(fmd.d_conf, "uuidlen", &lp->log_uuidlen);
1451052Sdilpreet 
1461052Sdilpreet 	lp->log_uuid = fmd_zalloc(lp->log_uuidlen + 1, FMD_SLEEP);
1471052Sdilpreet 	uuid_generate(uuid);
1481052Sdilpreet 	uuid_unparse(uuid, lp->log_uuid);
1490Sstevel@tonic-gate 
1500Sstevel@tonic-gate 	err |= ea_set_group(&hdr, CAT_FMA_GROUP);
1510Sstevel@tonic-gate 	err |= ea_set_group(&toc, CAT_FMA_GROUP);
1520Sstevel@tonic-gate 
1530Sstevel@tonic-gate 	err |= ea_set_item(&i0, CAT_FMA_LABEL, tag, 0);
1540Sstevel@tonic-gate 	err |= ea_set_item(&i1, CAT_FMA_VERSION, fmd.d_version, 0);
1550Sstevel@tonic-gate 	err |= ea_set_item(&i2, CAT_FMA_OSREL, osrel, 0);
1560Sstevel@tonic-gate 	err |= ea_set_item(&i3, CAT_FMA_OSVER, osver, 0);
1570Sstevel@tonic-gate 	err |= ea_set_item(&i4, CAT_FMA_PLAT, plat, 0);
1581052Sdilpreet 	err |= ea_set_item(&i5, CAT_FMA_UUID, lp->log_uuid, 0);
1591052Sdilpreet 	err |= ea_set_item(&i6, CAT_FMA_OFFSET, &off, 0);
1600Sstevel@tonic-gate 
1610Sstevel@tonic-gate 	(void) ea_attach_to_group(&hdr, &i0);
1620Sstevel@tonic-gate 	(void) ea_attach_to_group(&hdr, &i1);
1630Sstevel@tonic-gate 	(void) ea_attach_to_group(&hdr, &i2);
1640Sstevel@tonic-gate 	(void) ea_attach_to_group(&hdr, &i3);
1650Sstevel@tonic-gate 	(void) ea_attach_to_group(&hdr, &i4);
1661052Sdilpreet 	(void) ea_attach_to_group(&hdr, &i5);
1671052Sdilpreet 	(void) ea_attach_to_group(&toc, &i6);
1680Sstevel@tonic-gate 
1690Sstevel@tonic-gate 	if (err == 0) {
1700Sstevel@tonic-gate 		size_t hdr_size = ea_pack_object(&hdr, NULL, 0);
1710Sstevel@tonic-gate 		size_t toc_size = ea_pack_object(&toc, NULL, 0);
1720Sstevel@tonic-gate 
1730Sstevel@tonic-gate 		size_t size = hdr_size + toc_size;
1740Sstevel@tonic-gate 		void *buf = fmd_alloc(size, FMD_SLEEP);
1750Sstevel@tonic-gate 
1760Sstevel@tonic-gate 		(void) ea_pack_object(&hdr, buf, hdr_size);
1770Sstevel@tonic-gate 		(void) ea_pack_object(&toc, (char *)buf + hdr_size, toc_size);
1780Sstevel@tonic-gate 
1790Sstevel@tonic-gate 		if ((lp->log_off = lseek64(lp->log_fd, 0, SEEK_END)) == -1L)
1800Sstevel@tonic-gate 			fmd_panic("failed to seek log %s", lp->log_name);
1810Sstevel@tonic-gate 
1820Sstevel@tonic-gate 		if (fmd_log_write(lp, buf, size) != size)
1830Sstevel@tonic-gate 			err = errno; /* save errno for fmd_set_errno() below */
1840Sstevel@tonic-gate 
1850Sstevel@tonic-gate 		fmd_free(buf, size);
1860Sstevel@tonic-gate 
1870Sstevel@tonic-gate 		lp->log_toc = lp->log_off + hdr_size;
1880Sstevel@tonic-gate 		lp->log_beg = lp->log_off + hdr_size + toc_size;
1890Sstevel@tonic-gate 		lp->log_off = lp->log_off + hdr_size + toc_size;
1900Sstevel@tonic-gate 
1910Sstevel@tonic-gate 		if (lp->log_off != lseek64(lp->log_fd, 0, SEEK_END))
1920Sstevel@tonic-gate 			fmd_panic("eof off != log_off 0x%llx\n", lp->log_off);
1930Sstevel@tonic-gate 	} else
1940Sstevel@tonic-gate 		err = EFMD_LOG_EXACCT;
1950Sstevel@tonic-gate 
1960Sstevel@tonic-gate 	(void) ea_free_item(&i0, EUP_ALLOC);
1970Sstevel@tonic-gate 	(void) ea_free_item(&i1, EUP_ALLOC);
1980Sstevel@tonic-gate 	(void) ea_free_item(&i2, EUP_ALLOC);
1990Sstevel@tonic-gate 	(void) ea_free_item(&i3, EUP_ALLOC);
2000Sstevel@tonic-gate 	(void) ea_free_item(&i4, EUP_ALLOC);
2010Sstevel@tonic-gate 	(void) ea_free_item(&i5, EUP_ALLOC);
2021052Sdilpreet 	(void) ea_free_item(&i6, EUP_ALLOC);
2030Sstevel@tonic-gate 
2040Sstevel@tonic-gate 	return (err ? fmd_set_errno(err) : 0);
2050Sstevel@tonic-gate }
2060Sstevel@tonic-gate 
2070Sstevel@tonic-gate static int
fmd_log_check_err(fmd_log_t * lp,int err,const char * msg)2080Sstevel@tonic-gate fmd_log_check_err(fmd_log_t *lp, int err, const char *msg)
2090Sstevel@tonic-gate {
2100Sstevel@tonic-gate 	int eaerr = ea_error();
2110Sstevel@tonic-gate 	char buf[BUFSIZ];
2120Sstevel@tonic-gate 
2130Sstevel@tonic-gate 	(void) snprintf(buf, sizeof (buf), "%s: %s: %s\n",
2140Sstevel@tonic-gate 	    lp->log_name, msg, eaerr != EXR_OK ?
2150Sstevel@tonic-gate 	    fmd_ea_strerror(eaerr) : "catalog tag mismatch");
2160Sstevel@tonic-gate 
2170Sstevel@tonic-gate 	fmd_error(err, buf);
2180Sstevel@tonic-gate 	return (fmd_set_errno(err));
2190Sstevel@tonic-gate }
2200Sstevel@tonic-gate 
2210Sstevel@tonic-gate static int
fmd_log_check_hdr(fmd_log_t * lp,const char * tag)2220Sstevel@tonic-gate fmd_log_check_hdr(fmd_log_t *lp, const char *tag)
2230Sstevel@tonic-gate {
2240Sstevel@tonic-gate 	int got_version = 0, got_label = 0;
2250Sstevel@tonic-gate 	ea_object_t *grp, *obj;
2260Sstevel@tonic-gate 	off64_t hdr_off, hdr_size;
2270Sstevel@tonic-gate 	int dvers, fvers;
2280Sstevel@tonic-gate 	const char *p;
2290Sstevel@tonic-gate 
2300Sstevel@tonic-gate 	ea_clear(&lp->log_ea); /* resync exacct file */
2310Sstevel@tonic-gate 
2320Sstevel@tonic-gate 	if ((hdr_off = lseek64(lp->log_fd, 0, SEEK_CUR)) == -1L)
2330Sstevel@tonic-gate 		fmd_panic("failed to seek log %s", lp->log_name);
2340Sstevel@tonic-gate 
2350Sstevel@tonic-gate 	/*
2360Sstevel@tonic-gate 	 * Read the first group of log meta-data: the write-once read-only
2370Sstevel@tonic-gate 	 * file header.  We read all records in this group, ignoring all but
2380Sstevel@tonic-gate 	 * the VERSION and LABEL, which are required and must be verified.
2390Sstevel@tonic-gate 	 */
2400Sstevel@tonic-gate 	if ((grp = ea_get_object_tree(&lp->log_ea, 1)) == NULL ||
2410Sstevel@tonic-gate 	    grp->eo_catalog != CAT_FMA_GROUP) {
2420Sstevel@tonic-gate 		ea_free_object(grp, EUP_ALLOC);
2430Sstevel@tonic-gate 		return (fmd_log_check_err(lp, EFMD_LOG_INVAL,
2440Sstevel@tonic-gate 		    "invalid fma hdr record group"));
2450Sstevel@tonic-gate 	}
2460Sstevel@tonic-gate 
2470Sstevel@tonic-gate 	for (obj = grp->eo_group.eg_objs; obj != NULL; obj = obj->eo_next) {
2480Sstevel@tonic-gate 		switch (obj->eo_catalog) {
2490Sstevel@tonic-gate 		case CAT_FMA_VERSION:
2500Sstevel@tonic-gate 			for (dvers = 0, p = fmd.d_version;
2510Sstevel@tonic-gate 			    *p != '\0'; p++) {
2520Sstevel@tonic-gate 				if (isdigit(*p))
2530Sstevel@tonic-gate 					dvers = dvers * 10 + (*p - '0');
2540Sstevel@tonic-gate 				else
2550Sstevel@tonic-gate 					break;
2560Sstevel@tonic-gate 			}
2570Sstevel@tonic-gate 
2580Sstevel@tonic-gate 			for (fvers = 0, p = obj->eo_item.ei_string;
2590Sstevel@tonic-gate 			    *p != '\0'; p++) {
2600Sstevel@tonic-gate 				if (isdigit(*p))
2610Sstevel@tonic-gate 					fvers = fvers * 10 + (*p - '0');
2620Sstevel@tonic-gate 				else
2630Sstevel@tonic-gate 					break;
2640Sstevel@tonic-gate 			}
2650Sstevel@tonic-gate 
2660Sstevel@tonic-gate 			if (fvers > dvers) {
2670Sstevel@tonic-gate 				fmd_error(EFMD_LOG_INVAL, "%s: log version "
2680Sstevel@tonic-gate 				    "%s is not supported by this daemon\n",
2690Sstevel@tonic-gate 				    lp->log_name, obj->eo_item.ei_string);
2700Sstevel@tonic-gate 				ea_free_object(grp, EUP_ALLOC);
2710Sstevel@tonic-gate 				return (fmd_set_errno(EFMD_LOG_VERSION));
2720Sstevel@tonic-gate 			}
2730Sstevel@tonic-gate 
2740Sstevel@tonic-gate 			got_version++;
2750Sstevel@tonic-gate 			break;
2760Sstevel@tonic-gate 
2770Sstevel@tonic-gate 		case CAT_FMA_LABEL:
2780Sstevel@tonic-gate 			if (strcmp(obj->eo_item.ei_string, tag) != 0) {
2790Sstevel@tonic-gate 				fmd_error(EFMD_LOG_INVAL, "%s: log tag '%s' "
2800Sstevel@tonic-gate 				    "does not matched expected tag '%s'\n",
2810Sstevel@tonic-gate 				    lp->log_name, obj->eo_item.ei_string, tag);
2820Sstevel@tonic-gate 				ea_free_object(grp, EUP_ALLOC);
2830Sstevel@tonic-gate 				return (fmd_set_errno(EFMD_LOG_INVAL));
2840Sstevel@tonic-gate 			}
2850Sstevel@tonic-gate 			got_label++;
2860Sstevel@tonic-gate 			break;
2871052Sdilpreet 		case CAT_FMA_UUID:
2881052Sdilpreet 			lp->log_uuid = fmd_strdup(obj->eo_item.ei_string,
2891052Sdilpreet 			    FMD_SLEEP);
2901052Sdilpreet 			lp->log_uuidlen = strlen(lp->log_uuid);
2911052Sdilpreet 			break;
2920Sstevel@tonic-gate 		}
2930Sstevel@tonic-gate 	}
2940Sstevel@tonic-gate 
2950Sstevel@tonic-gate 	hdr_size = ea_pack_object(grp, NULL, 0);
2960Sstevel@tonic-gate 	ea_free_object(grp, EUP_ALLOC);
2970Sstevel@tonic-gate 
2980Sstevel@tonic-gate 	if (!got_version || !got_label) {
2990Sstevel@tonic-gate 		fmd_error(EFMD_LOG_INVAL, "%s: fmd hdr record group did not "
3000Sstevel@tonic-gate 		    "include mandatory version and/or label\n", lp->log_name);
3010Sstevel@tonic-gate 		return (fmd_set_errno(EFMD_LOG_INVAL));
3020Sstevel@tonic-gate 	}
3030Sstevel@tonic-gate 
3040Sstevel@tonic-gate 	/*
3050Sstevel@tonic-gate 	 * Read the second group of log meta-data: the table of contents.  We
3060Sstevel@tonic-gate 	 * expect this group to contain an OFFSET object indicating the current
3070Sstevel@tonic-gate 	 * value of log_skip.  We save this in our fmd_log_t and then return.
3080Sstevel@tonic-gate 	 */
3090Sstevel@tonic-gate 	if ((grp = ea_get_object_tree(&lp->log_ea, 1)) == NULL ||
3100Sstevel@tonic-gate 	    grp->eo_catalog != CAT_FMA_GROUP || grp->eo_group.eg_nobjs < 1 ||
3110Sstevel@tonic-gate 	    grp->eo_group.eg_objs->eo_catalog != CAT_FMA_OFFSET) {
3120Sstevel@tonic-gate 		ea_free_object(grp, EUP_ALLOC);
3130Sstevel@tonic-gate 		return (fmd_log_check_err(lp, EFMD_LOG_INVAL,
3140Sstevel@tonic-gate 		    "invalid fma toc record group"));
3150Sstevel@tonic-gate 	}
3160Sstevel@tonic-gate 
3170Sstevel@tonic-gate 	lp->log_toc = hdr_off + hdr_size;
3180Sstevel@tonic-gate 	lp->log_beg = hdr_off + hdr_size + ea_pack_object(grp, NULL, 0);
3190Sstevel@tonic-gate 	lp->log_off = lseek64(lp->log_fd, 0, SEEK_END);
3200Sstevel@tonic-gate 	lp->log_skip = grp->eo_group.eg_objs->eo_item.ei_uint64;
3210Sstevel@tonic-gate 
3220Sstevel@tonic-gate 	if (lp->log_skip > lp->log_off) {
3230Sstevel@tonic-gate 		fmd_error(EFMD_LOG_INVAL, "%s: skip %llx exceeds file size; "
3240Sstevel@tonic-gate 		    "resetting to zero\n", lp->log_name, lp->log_skip);
3250Sstevel@tonic-gate 		lp->log_skip = 0;
3260Sstevel@tonic-gate 	}
3270Sstevel@tonic-gate 
3280Sstevel@tonic-gate 	ea_free_object(grp, EUP_ALLOC);
3290Sstevel@tonic-gate 	return (0);
3300Sstevel@tonic-gate }
3310Sstevel@tonic-gate 
3320Sstevel@tonic-gate static int
fmd_log_open_exacct(fmd_log_t * lp,int aflags,int oflags)3330Sstevel@tonic-gate fmd_log_open_exacct(fmd_log_t *lp, int aflags, int oflags)
3340Sstevel@tonic-gate {
3350Sstevel@tonic-gate 	int fd = dup(lp->log_fd);
3360Sstevel@tonic-gate 	const char *creator;
3370Sstevel@tonic-gate 
3380Sstevel@tonic-gate 	(void) fmd_conf_getprop(fmd.d_conf, "log.creator", &creator);
3390Sstevel@tonic-gate 
3400Sstevel@tonic-gate 	if (ea_fdopen(&lp->log_ea, fd, creator, aflags, oflags) != 0) {
3410Sstevel@tonic-gate 		fmd_error(EFMD_LOG_EXACCT, "%s: failed to open log file: %s\n",
3420Sstevel@tonic-gate 		    lp->log_name, fmd_ea_strerror(ea_error()));
3430Sstevel@tonic-gate 		(void) close(fd);
3440Sstevel@tonic-gate 		return (fmd_set_errno(EFMD_LOG_EXACCT));
3450Sstevel@tonic-gate 	}
3460Sstevel@tonic-gate 
3470Sstevel@tonic-gate 	lp->log_flags |= FMD_LF_EAOPEN;
3480Sstevel@tonic-gate 	return (0);
3490Sstevel@tonic-gate }
3500Sstevel@tonic-gate 
3510Sstevel@tonic-gate static fmd_log_t *
fmd_log_xopen(const char * root,const char * name,const char * tag,int oflags)3520Sstevel@tonic-gate fmd_log_xopen(const char *root, const char *name, const char *tag, int oflags)
3530Sstevel@tonic-gate {
3540Sstevel@tonic-gate 	fmd_log_t *lp = fmd_zalloc(sizeof (fmd_log_t), FMD_SLEEP);
3550Sstevel@tonic-gate 
3560Sstevel@tonic-gate 	char buf[PATH_MAX];
357*3323Scindi 	char *slash = "/";
3580Sstevel@tonic-gate 	size_t len;
3590Sstevel@tonic-gate 	int err;
3600Sstevel@tonic-gate 
3610Sstevel@tonic-gate 	(void) pthread_mutex_init(&lp->log_lock, NULL);
3620Sstevel@tonic-gate 	(void) pthread_cond_init(&lp->log_cv, NULL);
3630Sstevel@tonic-gate 	(void) pthread_mutex_lock(&lp->log_lock);
3640Sstevel@tonic-gate 
365*3323Scindi 	if (strcmp(root, "") == 0)
366*3323Scindi 		slash = "";
367*3323Scindi 	len = strlen(root) + strlen(name) + strlen(slash) + 1; /* for "\0" */
3680Sstevel@tonic-gate 	lp->log_name = fmd_alloc(len, FMD_SLEEP);
369*3323Scindi 	(void) snprintf(lp->log_name, len, "%s%s%s", root, slash, name);
3700Sstevel@tonic-gate 	lp->log_tag = fmd_strdup(tag, FMD_SLEEP);
3710Sstevel@tonic-gate 	(void) fmd_conf_getprop(fmd.d_conf, "log.minfree", &lp->log_minfree);
3720Sstevel@tonic-gate 
3730Sstevel@tonic-gate 	if (strcmp(lp->log_tag, FMD_LOG_ERROR) == 0)
3740Sstevel@tonic-gate 		lp->log_flags |= FMD_LF_REPLAY;
3750Sstevel@tonic-gate 
3761193Smws 	if (strcmp(lp->log_tag, FMD_LOG_XPRT) == 0)
3771193Smws 		oflags &= ~O_SYNC;
3781193Smws 
3790Sstevel@tonic-gate top:
3800Sstevel@tonic-gate 	if ((lp->log_fd = open64(lp->log_name, oflags, 0644)) == -1 ||
3810Sstevel@tonic-gate 	    fstat64(lp->log_fd, &lp->log_stat) == -1) {
3820Sstevel@tonic-gate 		fmd_error(EFMD_LOG_OPEN, "failed to open log %s", lp->log_name);
3830Sstevel@tonic-gate 		fmd_log_close(lp);
3840Sstevel@tonic-gate 		return (NULL);
3850Sstevel@tonic-gate 	}
3860Sstevel@tonic-gate 
3870Sstevel@tonic-gate 	/*
3880Sstevel@tonic-gate 	 * If our open() created the log file, use libexacct to write a header
3890Sstevel@tonic-gate 	 * and position the file just after the header (EO_TAIL).  If the log
3900Sstevel@tonic-gate 	 * file already existed, use libexacct to validate the header and again
3910Sstevel@tonic-gate 	 * position the file just after the header (EO_HEAD).  Note that we lie
3920Sstevel@tonic-gate 	 * to libexacct about 'oflags' in order to achieve the desired result.
3930Sstevel@tonic-gate 	 */
3940Sstevel@tonic-gate 	if (lp->log_stat.st_size == 0) {
3950Sstevel@tonic-gate 		err = fmd_log_open_exacct(lp, EO_VALID_HDR | EO_TAIL,
3960Sstevel@tonic-gate 		    O_CREAT | O_WRONLY) || fmd_log_write_hdr(lp, tag);
3970Sstevel@tonic-gate 	} else {
3980Sstevel@tonic-gate 		err = fmd_log_open_exacct(lp, EO_VALID_HDR | EO_HEAD,
3990Sstevel@tonic-gate 		    O_RDONLY) || fmd_log_check_hdr(lp, tag);
4000Sstevel@tonic-gate 	}
4010Sstevel@tonic-gate 
4020Sstevel@tonic-gate 	/*
4030Sstevel@tonic-gate 	 * If ea_fdopen() failed and the log was pre-existing, attempt to move
4040Sstevel@tonic-gate 	 * it aside and start a new one.  If we created the log but failed to
4050Sstevel@tonic-gate 	 * initialize it, then we have no choice but to give up (e.g. EROFS).
4060Sstevel@tonic-gate 	 */
4070Sstevel@tonic-gate 	if (err) {
4080Sstevel@tonic-gate 		fmd_error(EFMD_LOG_OPEN,
4090Sstevel@tonic-gate 		    "failed to initialize log %s", lp->log_name);
4100Sstevel@tonic-gate 
4110Sstevel@tonic-gate 		if (lp->log_flags & FMD_LF_EAOPEN) {
4120Sstevel@tonic-gate 			lp->log_flags &= ~FMD_LF_EAOPEN;
4130Sstevel@tonic-gate 			(void) ea_close(&lp->log_ea);
4140Sstevel@tonic-gate 		}
4150Sstevel@tonic-gate 
4160Sstevel@tonic-gate 		(void) close(lp->log_fd);
4170Sstevel@tonic-gate 		lp->log_fd = -1;
4180Sstevel@tonic-gate 
4190Sstevel@tonic-gate 		if (lp->log_stat.st_size != 0 && snprintf(buf,
4200Sstevel@tonic-gate 		    sizeof (buf), "%s-", lp->log_name) < PATH_MAX &&
4210Sstevel@tonic-gate 		    rename(lp->log_name, buf) == 0) {
4220Sstevel@tonic-gate 			TRACE((FMD_DBG_LOG, "mv %s to %s", lp->log_name, buf));
4230Sstevel@tonic-gate 			if (oflags & O_CREAT)
4240Sstevel@tonic-gate 				goto top;
4250Sstevel@tonic-gate 		}
4260Sstevel@tonic-gate 
4270Sstevel@tonic-gate 		fmd_log_close(lp);
4280Sstevel@tonic-gate 		return (NULL);
4290Sstevel@tonic-gate 	}
4300Sstevel@tonic-gate 
4310Sstevel@tonic-gate 	lp->log_refs++;
4320Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&lp->log_lock);
4330Sstevel@tonic-gate 
4340Sstevel@tonic-gate 	return (lp);
4350Sstevel@tonic-gate }
4360Sstevel@tonic-gate 
4370Sstevel@tonic-gate fmd_log_t *
fmd_log_tryopen(const char * root,const char * name,const char * tag)4380Sstevel@tonic-gate fmd_log_tryopen(const char *root, const char *name, const char *tag)
4390Sstevel@tonic-gate {
4400Sstevel@tonic-gate 	return (fmd_log_xopen(root, name, tag, O_RDWR | O_SYNC));
4410Sstevel@tonic-gate }
4420Sstevel@tonic-gate 
4430Sstevel@tonic-gate fmd_log_t *
fmd_log_open(const char * root,const char * name,const char * tag)4440Sstevel@tonic-gate fmd_log_open(const char *root, const char *name, const char *tag)
4450Sstevel@tonic-gate {
4460Sstevel@tonic-gate 	return (fmd_log_xopen(root, name, tag, O_RDWR | O_CREAT | O_SYNC));
4470Sstevel@tonic-gate }
4480Sstevel@tonic-gate 
4490Sstevel@tonic-gate void
fmd_log_close(fmd_log_t * lp)4500Sstevel@tonic-gate fmd_log_close(fmd_log_t *lp)
4510Sstevel@tonic-gate {
4520Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&lp->log_lock));
4530Sstevel@tonic-gate 	ASSERT(lp->log_refs == 0);
4540Sstevel@tonic-gate 
4550Sstevel@tonic-gate 	if ((lp->log_flags & FMD_LF_EAOPEN) && ea_close(&lp->log_ea) != 0) {
4560Sstevel@tonic-gate 		fmd_error(EFMD_LOG_CLOSE, "failed to close log %s: %s\n",
4570Sstevel@tonic-gate 		    lp->log_name, fmd_ea_strerror(ea_error()));
4580Sstevel@tonic-gate 	}
4590Sstevel@tonic-gate 
4600Sstevel@tonic-gate 	if (lp->log_fd >= 0 && close(lp->log_fd) != 0) {
4610Sstevel@tonic-gate 		fmd_error(EFMD_LOG_CLOSE,
4620Sstevel@tonic-gate 		    "failed to close log %s", lp->log_name);
4630Sstevel@tonic-gate 	}
4640Sstevel@tonic-gate 
4650Sstevel@tonic-gate 	fmd_strfree(lp->log_name);
4660Sstevel@tonic-gate 	fmd_strfree(lp->log_tag);
4671052Sdilpreet 	if (lp->log_uuid != NULL)
4681052Sdilpreet 		fmd_free(lp->log_uuid, lp->log_uuidlen + 1);
4690Sstevel@tonic-gate 
4700Sstevel@tonic-gate 	fmd_free(lp, sizeof (fmd_log_t));
4710Sstevel@tonic-gate }
4720Sstevel@tonic-gate 
4730Sstevel@tonic-gate void
fmd_log_hold_pending(fmd_log_t * lp)4740Sstevel@tonic-gate fmd_log_hold_pending(fmd_log_t *lp)
4750Sstevel@tonic-gate {
4760Sstevel@tonic-gate 	(void) pthread_mutex_lock(&lp->log_lock);
4770Sstevel@tonic-gate 
4780Sstevel@tonic-gate 	lp->log_refs++;
4790Sstevel@tonic-gate 	ASSERT(lp->log_refs != 0);
4800Sstevel@tonic-gate 
4810Sstevel@tonic-gate 	if (lp->log_flags & FMD_LF_REPLAY) {
4820Sstevel@tonic-gate 		lp->log_pending++;
4830Sstevel@tonic-gate 		ASSERT(lp->log_pending != 0);
4840Sstevel@tonic-gate 	}
4850Sstevel@tonic-gate 
4860Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&lp->log_lock);
4870Sstevel@tonic-gate }
4880Sstevel@tonic-gate 
4890Sstevel@tonic-gate void
fmd_log_hold(fmd_log_t * lp)4900Sstevel@tonic-gate fmd_log_hold(fmd_log_t *lp)
4910Sstevel@tonic-gate {
4920Sstevel@tonic-gate 	(void) pthread_mutex_lock(&lp->log_lock);
4930Sstevel@tonic-gate 	lp->log_refs++;
4940Sstevel@tonic-gate 	ASSERT(lp->log_refs != 0);
4950Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&lp->log_lock);
4960Sstevel@tonic-gate }
4970Sstevel@tonic-gate 
4980Sstevel@tonic-gate void
fmd_log_rele(fmd_log_t * lp)4990Sstevel@tonic-gate fmd_log_rele(fmd_log_t *lp)
5000Sstevel@tonic-gate {
5010Sstevel@tonic-gate 	(void) pthread_mutex_lock(&lp->log_lock);
5020Sstevel@tonic-gate 	ASSERT(lp->log_refs != 0);
5030Sstevel@tonic-gate 
5040Sstevel@tonic-gate 	if (--lp->log_refs == 0)
5050Sstevel@tonic-gate 		fmd_log_close(lp);
5060Sstevel@tonic-gate 	else
5070Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&lp->log_lock);
5080Sstevel@tonic-gate }
5090Sstevel@tonic-gate 
5100Sstevel@tonic-gate void
fmd_log_append(fmd_log_t * lp,fmd_event_t * e,fmd_case_t * cp)5110Sstevel@tonic-gate fmd_log_append(fmd_log_t *lp, fmd_event_t *e, fmd_case_t *cp)
5120Sstevel@tonic-gate {
5130Sstevel@tonic-gate 	fmd_event_impl_t *ep = (fmd_event_impl_t *)e;
5140Sstevel@tonic-gate 	fmd_case_impl_t *cip = (fmd_case_impl_t *)cp;
5150Sstevel@tonic-gate 	int err = 0;
5160Sstevel@tonic-gate 
5170Sstevel@tonic-gate 	ea_object_t grp0, grp1, i0, i1, i2, *items;
5181052Sdilpreet 	ea_object_t **fe = NULL;
5191052Sdilpreet 	size_t nvsize, easize, itsize, frsize;
5200Sstevel@tonic-gate 	char *nvbuf, *eabuf;
5210Sstevel@tonic-gate 	statvfs64_t stv;
5220Sstevel@tonic-gate 
5230Sstevel@tonic-gate 	(void) pthread_mutex_lock(&ep->ev_lock);
5240Sstevel@tonic-gate 
5250Sstevel@tonic-gate 	ASSERT(ep->ev_flags & FMD_EVF_VOLATILE);
5260Sstevel@tonic-gate 	ASSERT(ep->ev_log == NULL);
5270Sstevel@tonic-gate 
5280Sstevel@tonic-gate 	(void) nvlist_size(ep->ev_nvl, &nvsize, NV_ENCODE_XDR);
5290Sstevel@tonic-gate 	nvbuf = fmd_alloc(nvsize, FMD_SLEEP);
5300Sstevel@tonic-gate 	(void) nvlist_pack(ep->ev_nvl, &nvbuf, &nvsize, NV_ENCODE_XDR, 0);
5310Sstevel@tonic-gate 
5320Sstevel@tonic-gate 	if (lp->log_flags & FMD_LF_REPLAY)
5330Sstevel@tonic-gate 		err |= ea_set_group(&grp0, CAT_FMA_RGROUP);
5340Sstevel@tonic-gate 	else
5350Sstevel@tonic-gate 		err |= ea_set_group(&grp0, CAT_FMA_GROUP);
5360Sstevel@tonic-gate 
5370Sstevel@tonic-gate 	err |= ea_set_item(&i0, CAT_FMA_TODSEC, &ep->ev_time.ftv_sec, 0);
5380Sstevel@tonic-gate 	err |= ea_set_item(&i1, CAT_FMA_TODNSEC, &ep->ev_time.ftv_nsec, 0);
5390Sstevel@tonic-gate 	err |= ea_set_item(&i2, CAT_FMA_NVLIST, nvbuf, nvsize);
5400Sstevel@tonic-gate 
5410Sstevel@tonic-gate 	if (err != 0) {
5420Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&ep->ev_lock);
5430Sstevel@tonic-gate 		err = EFMD_LOG_EXACCT;
5440Sstevel@tonic-gate 		goto exerr;
5450Sstevel@tonic-gate 	}
5460Sstevel@tonic-gate 
5470Sstevel@tonic-gate 	(void) ea_attach_to_group(&grp0, &i0);
5480Sstevel@tonic-gate 	(void) ea_attach_to_group(&grp0, &i1);
5490Sstevel@tonic-gate 	(void) ea_attach_to_group(&grp0, &i2);
5500Sstevel@tonic-gate 
5510Sstevel@tonic-gate 	/*
5520Sstevel@tonic-gate 	 * If this event has a case associated with it (i.e. it is a list),
5530Sstevel@tonic-gate 	 * then allocate a block of ea_object_t's and fill in a group for
5540Sstevel@tonic-gate 	 * each event saved in the case's item list.  For each such group,
5550Sstevel@tonic-gate 	 * we attach it to grp1, which in turn will be attached to grp0.
5560Sstevel@tonic-gate 	 */
5570Sstevel@tonic-gate 	if (cp != NULL) {
5581052Sdilpreet 		ea_object_t *egrp, *ip, **fp;
5590Sstevel@tonic-gate 		fmd_event_impl_t *eip;
5600Sstevel@tonic-gate 		fmd_case_item_t *cit;
5610Sstevel@tonic-gate 
5620Sstevel@tonic-gate 		(void) ea_set_group(&grp1, CAT_FMA_GROUP);
5631052Sdilpreet 		frsize = sizeof (ea_object_t *) * cip->ci_nitems;
5640Sstevel@tonic-gate 		itsize = sizeof (ea_object_t) * cip->ci_nitems * 5;
5650Sstevel@tonic-gate 		items = ip = fmd_alloc(itsize, FMD_SLEEP);
5660Sstevel@tonic-gate 
5670Sstevel@tonic-gate 		for (cit = cip->ci_items; cit != NULL; cit = cit->cit_next) {
5680Sstevel@tonic-gate 			major_t maj;
5690Sstevel@tonic-gate 			minor_t min;
5700Sstevel@tonic-gate 
5710Sstevel@tonic-gate 			eip = (fmd_event_impl_t *)cit->cit_event;
5720Sstevel@tonic-gate 
5730Sstevel@tonic-gate 			if (eip->ev_log == NULL)
5740Sstevel@tonic-gate 				continue; /* event was never logged */
5750Sstevel@tonic-gate 
5760Sstevel@tonic-gate 			maj = major(eip->ev_log->log_stat.st_dev);
5770Sstevel@tonic-gate 			min = minor(eip->ev_log->log_stat.st_dev);
5780Sstevel@tonic-gate 
5790Sstevel@tonic-gate 			(void) ea_set_group(ip, CAT_FMA_GROUP);
5800Sstevel@tonic-gate 			egrp = ip++; /* first obj is group */
5810Sstevel@tonic-gate 
5821052Sdilpreet 			/*
5831052Sdilpreet 			 * If the event log file is in legacy format,
5841052Sdilpreet 			 * then write the xref to the file in the legacy
5851052Sdilpreet 			 * maj/min/inode method else write it using the
5861052Sdilpreet 			 * file uuid.
5871052Sdilpreet 			 */
5881052Sdilpreet 			if (eip->ev_log->log_uuid == NULL) {
5891052Sdilpreet 				(void) ea_set_item(ip, CAT_FMA_MAJOR, &maj, 0);
5901052Sdilpreet 				(void) ea_attach_to_group(egrp, ip++);
5911052Sdilpreet 				(void) ea_set_item(ip, CAT_FMA_MINOR, &min, 0);
5921052Sdilpreet 				(void) ea_attach_to_group(egrp, ip++);
5931052Sdilpreet 				(void) ea_set_item(ip, CAT_FMA_INODE,
5941052Sdilpreet 				    &eip->ev_log->log_stat.st_ino, 0);
5951052Sdilpreet 				(void) ea_attach_to_group(egrp, ip++);
5961052Sdilpreet 			} else {
5971052Sdilpreet 				if (ea_set_item(ip, CAT_FMA_UUID,
5981052Sdilpreet 				    eip->ev_log->log_uuid, 0) == -1) {
5991052Sdilpreet 					err = EFMD_LOG_EXACCT;
6001052Sdilpreet 					goto exerrcp;
6011052Sdilpreet 				}
6021052Sdilpreet 				if (fe == NULL)
6031052Sdilpreet 					fe = fp = fmd_zalloc(frsize, FMD_SLEEP);
6041052Sdilpreet 				*fp++ = ip;
6051052Sdilpreet 				(void) ea_attach_to_group(egrp, ip++);
6061052Sdilpreet 			}
6070Sstevel@tonic-gate 			(void) ea_set_item(ip, CAT_FMA_OFFSET, &eip->ev_off, 0);
6080Sstevel@tonic-gate 			(void) ea_attach_to_group(egrp, ip++);
6090Sstevel@tonic-gate 			(void) ea_attach_to_group(&grp1, egrp);
6100Sstevel@tonic-gate 		}
6110Sstevel@tonic-gate 		(void) ea_attach_to_group(&grp0, &grp1);
6120Sstevel@tonic-gate 	}
6130Sstevel@tonic-gate 
6140Sstevel@tonic-gate 	easize = ea_pack_object(&grp0, NULL, 0);
6150Sstevel@tonic-gate 	eabuf = fmd_alloc(easize, FMD_SLEEP);
6160Sstevel@tonic-gate 	(void) ea_pack_object(&grp0, eabuf, easize);
6170Sstevel@tonic-gate 
6180Sstevel@tonic-gate 	/*
6190Sstevel@tonic-gate 	 * Before writing the record, check to see if this would cause the free
6200Sstevel@tonic-gate 	 * space in the filesystem to drop below our minfree threshold.  If so,
6210Sstevel@tonic-gate 	 * don't bother attempting the write and instead pretend it failed.  As
6220Sstevel@tonic-gate 	 * fmd(1M) runs as root, it will be able to access the space "reserved"
6230Sstevel@tonic-gate 	 * for root, and therefore can run the system of out of disk space in a
6240Sstevel@tonic-gate 	 * heavy error load situation, violating the basic design principle of
6250Sstevel@tonic-gate 	 * fmd(1M) that we don't want to make a bad situation even worse.
6260Sstevel@tonic-gate 	 */
6270Sstevel@tonic-gate 	(void) pthread_mutex_lock(&lp->log_lock);
6280Sstevel@tonic-gate 
6290Sstevel@tonic-gate 	if (lp->log_minfree != 0 && fstatvfs64(lp->log_fd, &stv) == 0 &&
6300Sstevel@tonic-gate 	    stv.f_bavail * stv.f_frsize < lp->log_minfree + easize) {
6310Sstevel@tonic-gate 
6320Sstevel@tonic-gate 		TRACE((FMD_DBG_LOG, "append %s crosses minfree", lp->log_tag));
6330Sstevel@tonic-gate 		err = EFMD_LOG_MINFREE;
6340Sstevel@tonic-gate 
6350Sstevel@tonic-gate 	} else if (fmd_log_write(lp, eabuf, easize) == easize) {
6360Sstevel@tonic-gate 		TRACE((FMD_DBG_LOG, "append %s %p off=0x%llx",
6370Sstevel@tonic-gate 		    lp->log_tag, (void *)ep, (u_longlong_t)lp->log_off));
6380Sstevel@tonic-gate 
6390Sstevel@tonic-gate 		ep->ev_flags &= ~FMD_EVF_VOLATILE;
6400Sstevel@tonic-gate 		ep->ev_log = lp;
6410Sstevel@tonic-gate 		ep->ev_off = lp->log_off;
6420Sstevel@tonic-gate 		ep->ev_len = easize;
6430Sstevel@tonic-gate 
6440Sstevel@tonic-gate 		if (lp->log_flags & FMD_LF_REPLAY) {
6450Sstevel@tonic-gate 			lp->log_pending++;
6460Sstevel@tonic-gate 			ASSERT(lp->log_pending != 0);
6470Sstevel@tonic-gate 		}
6480Sstevel@tonic-gate 
6490Sstevel@tonic-gate 		lp->log_refs++;
6500Sstevel@tonic-gate 		ASSERT(lp->log_refs != 0);
6510Sstevel@tonic-gate 		lp->log_off += easize;
6520Sstevel@tonic-gate 	} else {
6530Sstevel@tonic-gate 		err = errno; /* save errno for fmd_error() call below */
6540Sstevel@tonic-gate 
6550Sstevel@tonic-gate 		/*
6560Sstevel@tonic-gate 		 * If we can't write append the record, seek the file back to
6570Sstevel@tonic-gate 		 * the original location and truncate it there in order to make
6580Sstevel@tonic-gate 		 * sure the file is always in a sane state w.r.t. libexacct.
6590Sstevel@tonic-gate 		 */
6600Sstevel@tonic-gate 		(void) lseek64(lp->log_fd, lp->log_off, SEEK_SET);
6610Sstevel@tonic-gate 		(void) ftruncate64(lp->log_fd, lp->log_off);
6620Sstevel@tonic-gate 	}
6630Sstevel@tonic-gate 
6640Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&lp->log_lock);
6650Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&ep->ev_lock);
6660Sstevel@tonic-gate 
6671052Sdilpreet 	fmd_free(eabuf, easize);
6681052Sdilpreet 
6691052Sdilpreet exerrcp:
6701052Sdilpreet 	if (cp != NULL) {
6711052Sdilpreet 		if (fe != NULL) {
6721052Sdilpreet 			ea_object_t **fp = fe;
6731052Sdilpreet 			int i = 0;
6741052Sdilpreet 
6751052Sdilpreet 			for (; *fp != NULL && i < cip->ci_nitems; i++)
6761052Sdilpreet 				(void) ea_free_item(*fp++, EUP_ALLOC);
6771052Sdilpreet 			fmd_free(fe, frsize);
6781052Sdilpreet 		}
6791052Sdilpreet 
6800Sstevel@tonic-gate 		fmd_free(items, itsize);
6811052Sdilpreet 	}
6820Sstevel@tonic-gate 
6830Sstevel@tonic-gate exerr:
6840Sstevel@tonic-gate 	fmd_free(nvbuf, nvsize);
6850Sstevel@tonic-gate 
6860Sstevel@tonic-gate 	(void) ea_free_item(&i0, EUP_ALLOC);
6870Sstevel@tonic-gate 	(void) ea_free_item(&i1, EUP_ALLOC);
6880Sstevel@tonic-gate 	(void) ea_free_item(&i2, EUP_ALLOC);
6890Sstevel@tonic-gate 
6900Sstevel@tonic-gate 	/*
6910Sstevel@tonic-gate 	 * Keep track of out-of-space errors using global statistics.  As we're
6920Sstevel@tonic-gate 	 * out of disk space, it's unlikely the EFMD_LOG_APPEND will be logged.
6930Sstevel@tonic-gate 	 */
6940Sstevel@tonic-gate 	if (err == ENOSPC || err == EFMD_LOG_MINFREE) {
6950Sstevel@tonic-gate 		fmd_stat_t *sp;
6960Sstevel@tonic-gate 
6970Sstevel@tonic-gate 		if (lp == fmd.d_errlog)
6980Sstevel@tonic-gate 			sp = &fmd.d_stats->ds_err_enospc;
6990Sstevel@tonic-gate 		else if (lp == fmd.d_fltlog)
7000Sstevel@tonic-gate 			sp = &fmd.d_stats->ds_flt_enospc;
7010Sstevel@tonic-gate 		else
7020Sstevel@tonic-gate 			sp = &fmd.d_stats->ds_oth_enospc;
7030Sstevel@tonic-gate 
7040Sstevel@tonic-gate 		(void) pthread_mutex_lock(&fmd.d_stats_lock);
7050Sstevel@tonic-gate 		sp->fmds_value.ui64++;
7060Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&fmd.d_stats_lock);
7070Sstevel@tonic-gate 	}
7080Sstevel@tonic-gate 
7090Sstevel@tonic-gate 	if (err != 0) {
7100Sstevel@tonic-gate 		fmd_error(EFMD_LOG_APPEND, "failed to log_append %s %p: %s\n",
7110Sstevel@tonic-gate 		    lp->log_tag, (void *)ep, fmd_strerror(err));
7120Sstevel@tonic-gate 	}
7130Sstevel@tonic-gate }
7140Sstevel@tonic-gate 
7150Sstevel@tonic-gate /*
7160Sstevel@tonic-gate  * Commit an event to the log permanently, indicating that it should not be
7170Sstevel@tonic-gate  * replayed on restart.  This is done by overwriting the event group's catalog
7180Sstevel@tonic-gate  * code with EXD_GROUP_FMA (from EXD_GROUP_RFMA used in fmd_log_append()).  We
7190Sstevel@tonic-gate  * use pwrite64() to update the existing word directly, using somewhat guilty
7200Sstevel@tonic-gate  * knowledge that exacct stores the 32-bit catalog word first for each object.
7210Sstevel@tonic-gate  * Since we are overwriting an existing log location using pwrite64() and hold
7220Sstevel@tonic-gate  * the event lock, we do not need to hold the log_lock during the i/o.
7230Sstevel@tonic-gate  */
7240Sstevel@tonic-gate void
fmd_log_commit(fmd_log_t * lp,fmd_event_t * e)7250Sstevel@tonic-gate fmd_log_commit(fmd_log_t *lp, fmd_event_t *e)
7260Sstevel@tonic-gate {
7270Sstevel@tonic-gate 	fmd_event_impl_t *ep = (fmd_event_impl_t *)e;
7280Sstevel@tonic-gate 	ea_catalog_t c;
7290Sstevel@tonic-gate 	int err = 0;
7300Sstevel@tonic-gate 
7310Sstevel@tonic-gate 	if (!(lp->log_flags & FMD_LF_REPLAY))
7320Sstevel@tonic-gate 		return; /* log does not require replay tagging */
7330Sstevel@tonic-gate 
7340Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&ep->ev_lock));
7350Sstevel@tonic-gate 	ASSERT(ep->ev_log == lp && ep->ev_off != 0);
7360Sstevel@tonic-gate 
7370Sstevel@tonic-gate 	c = CAT_FMA_GROUP;
7380Sstevel@tonic-gate 	exacct_order32(&c);
7390Sstevel@tonic-gate 
7400Sstevel@tonic-gate 	if (pwrite64(lp->log_fd, &c, sizeof (c), ep->ev_off) == sizeof (c)) {
7410Sstevel@tonic-gate 		TRACE((FMD_DBG_LOG, "commit %s %p", lp->log_tag, (void *)ep));
7420Sstevel@tonic-gate 		ep->ev_flags &= ~FMD_EVF_REPLAY;
7430Sstevel@tonic-gate 
7440Sstevel@tonic-gate 		/*
7450Sstevel@tonic-gate 		 * If we have committed the event, check to see if the TOC skip
7460Sstevel@tonic-gate 		 * offset needs to be updated, and decrement the pending count.
7470Sstevel@tonic-gate 		 */
7480Sstevel@tonic-gate 		(void) pthread_mutex_lock(&lp->log_lock);
7490Sstevel@tonic-gate 
7500Sstevel@tonic-gate 		if (lp->log_skip == ep->ev_off) {
7510Sstevel@tonic-gate 			lp->log_flags |= FMD_LF_DIRTY;
7520Sstevel@tonic-gate 			lp->log_skip += ep->ev_len;
7530Sstevel@tonic-gate 		}
7540Sstevel@tonic-gate 
7550Sstevel@tonic-gate 		ASSERT(lp->log_pending != 0);
7560Sstevel@tonic-gate 		lp->log_pending--;
7570Sstevel@tonic-gate 
7581193Smws 		(void) pthread_cond_broadcast(&lp->log_cv);
7590Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&lp->log_lock);
7600Sstevel@tonic-gate 
7610Sstevel@tonic-gate 	} else {
7620Sstevel@tonic-gate 		fmd_error(EFMD_LOG_COMMIT, "failed to log_commit %s %p: %s\n",
7630Sstevel@tonic-gate 		    lp->log_tag, (void *)ep, fmd_strerror(err));
7640Sstevel@tonic-gate 	}
7650Sstevel@tonic-gate }
7660Sstevel@tonic-gate 
7670Sstevel@tonic-gate /*
7680Sstevel@tonic-gate  * If we need to destroy an event and it wasn't able to be committed, we permit
7690Sstevel@tonic-gate  * the owner to decommit from ever trying again.  This operation decrements the
7700Sstevel@tonic-gate  * pending count on the log and broadcasts to anyone waiting on log_cv.
7710Sstevel@tonic-gate  */
7720Sstevel@tonic-gate void
fmd_log_decommit(fmd_log_t * lp,fmd_event_t * e)7730Sstevel@tonic-gate fmd_log_decommit(fmd_log_t *lp, fmd_event_t *e)
7740Sstevel@tonic-gate {
7750Sstevel@tonic-gate 	fmd_event_impl_t *ep = (fmd_event_impl_t *)e;
7760Sstevel@tonic-gate 
7770Sstevel@tonic-gate 	if (!(lp->log_flags & FMD_LF_REPLAY))
7780Sstevel@tonic-gate 		return; /* log does not require replay tagging */
7790Sstevel@tonic-gate 
7800Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&ep->ev_lock));
7810Sstevel@tonic-gate 	ASSERT(ep->ev_log == lp);
7820Sstevel@tonic-gate 
7830Sstevel@tonic-gate 	(void) pthread_mutex_lock(&lp->log_lock);
7840Sstevel@tonic-gate 
7850Sstevel@tonic-gate 	TRACE((FMD_DBG_LOG, "decommit %s %p", lp->log_tag, (void *)ep));
7860Sstevel@tonic-gate 	ep->ev_flags &= ~FMD_EVF_REPLAY;
7870Sstevel@tonic-gate 
7880Sstevel@tonic-gate 	ASSERT(lp->log_pending != 0);
7890Sstevel@tonic-gate 	lp->log_pending--;
7900Sstevel@tonic-gate 
7911193Smws 	(void) pthread_cond_broadcast(&lp->log_cv);
7920Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&lp->log_lock);
7930Sstevel@tonic-gate }
7940Sstevel@tonic-gate 
7950Sstevel@tonic-gate static fmd_event_t *
fmd_log_unpack(fmd_log_t * lp,ea_object_t * grp,off64_t off)7960Sstevel@tonic-gate fmd_log_unpack(fmd_log_t *lp, ea_object_t *grp, off64_t off)
7970Sstevel@tonic-gate {
7980Sstevel@tonic-gate 	fmd_timeval_t ftv = { -1ULL, -1ULL };
7990Sstevel@tonic-gate 	nvlist_t *nvl = NULL;
8000Sstevel@tonic-gate 
8010Sstevel@tonic-gate 	ea_object_t *obj;
8020Sstevel@tonic-gate 	char *class;
8030Sstevel@tonic-gate 	int err;
8040Sstevel@tonic-gate 
8050Sstevel@tonic-gate 	for (obj = grp->eo_group.eg_objs; obj != NULL; obj = obj->eo_next) {
8060Sstevel@tonic-gate 		switch (obj->eo_catalog) {
8070Sstevel@tonic-gate 		case CAT_FMA_NVLIST:
8080Sstevel@tonic-gate 			if ((err = nvlist_xunpack(obj->eo_item.ei_raw,
8090Sstevel@tonic-gate 			    obj->eo_item.ei_size, &nvl, &fmd.d_nva)) != 0) {
8100Sstevel@tonic-gate 				fmd_error(EFMD_LOG_UNPACK, "failed to unpack "
8110Sstevel@tonic-gate 				    "log nvpair: %s\n", fmd_strerror(err));
8120Sstevel@tonic-gate 				return (NULL);
8130Sstevel@tonic-gate 			}
8140Sstevel@tonic-gate 			break;
8150Sstevel@tonic-gate 
8160Sstevel@tonic-gate 		case CAT_FMA_TODSEC:
8170Sstevel@tonic-gate 			ftv.ftv_sec = obj->eo_item.ei_uint64;
8180Sstevel@tonic-gate 			break;
8190Sstevel@tonic-gate 
8200Sstevel@tonic-gate 		case CAT_FMA_TODNSEC:
8210Sstevel@tonic-gate 			ftv.ftv_nsec = obj->eo_item.ei_uint64;
8220Sstevel@tonic-gate 			break;
8230Sstevel@tonic-gate 		}
8240Sstevel@tonic-gate 	}
8250Sstevel@tonic-gate 
8260Sstevel@tonic-gate 	if (nvl == NULL || ftv.ftv_sec == -1ULL || ftv.ftv_nsec == -1ULL) {
8270Sstevel@tonic-gate 		fmd_error(EFMD_LOG_UNPACK, "failed to unpack log event: "
8280Sstevel@tonic-gate 		    "required object(s) missing from record group\n");
8290Sstevel@tonic-gate 		nvlist_free(nvl);
8300Sstevel@tonic-gate 		return (NULL);
8310Sstevel@tonic-gate 	}
8320Sstevel@tonic-gate 
8330Sstevel@tonic-gate 	if (nvlist_lookup_string(nvl, FM_CLASS, &class) != 0) {
8340Sstevel@tonic-gate 		fmd_error(EFMD_LOG_UNPACK, "failed to unpack log event: "
8350Sstevel@tonic-gate 		    "record is missing required '%s' nvpair\n", FM_CLASS);
8360Sstevel@tonic-gate 		nvlist_free(nvl);
8370Sstevel@tonic-gate 		return (NULL);
8380Sstevel@tonic-gate 	}
8390Sstevel@tonic-gate 
8400Sstevel@tonic-gate 	return (fmd_event_recreate(FMD_EVT_PROTOCOL,
8410Sstevel@tonic-gate 	    &ftv, nvl, class, lp, off, ea_pack_object(grp, NULL, 0)));
8420Sstevel@tonic-gate }
8430Sstevel@tonic-gate 
8440Sstevel@tonic-gate /*
8450Sstevel@tonic-gate  * Replay event(s) from the specified log by invoking the specified callback
8460Sstevel@tonic-gate  * function 'func' for each event.  If the log has the FMD_LF_REPLAY flag set,
8470Sstevel@tonic-gate  * we replay all events after log_skip that have the FMA_RGROUP group tag.
8480Sstevel@tonic-gate  * This mode is used for the error telemetry log.  If the log does not have
8490Sstevel@tonic-gate  * this flag set (used for ASRU logs), only the most recent event is replayed.
8500Sstevel@tonic-gate  */
8510Sstevel@tonic-gate void
fmd_log_replay(fmd_log_t * lp,fmd_log_f * func,void * data)8520Sstevel@tonic-gate fmd_log_replay(fmd_log_t *lp, fmd_log_f *func, void *data)
8530Sstevel@tonic-gate {
8540Sstevel@tonic-gate 	ea_object_t obj, *grp;
8550Sstevel@tonic-gate 	ea_object_type_t type;
8560Sstevel@tonic-gate 	ea_catalog_t c;
8570Sstevel@tonic-gate 	fmd_event_t *ep;
8580Sstevel@tonic-gate 	off64_t off, skp;
8590Sstevel@tonic-gate 	uint_t n = 0;
8600Sstevel@tonic-gate 
8610Sstevel@tonic-gate 	(void) pthread_mutex_lock(&lp->log_lock);
8620Sstevel@tonic-gate 
8630Sstevel@tonic-gate 	if (lp->log_stat.st_size == 0 && (lp->log_flags & FMD_LF_REPLAY)) {
8640Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&lp->log_lock);
8650Sstevel@tonic-gate 		return; /* we just created this log: never replay events */
8660Sstevel@tonic-gate 	}
8670Sstevel@tonic-gate 
8680Sstevel@tonic-gate 	while (lp->log_flags & FMD_LF_BUSY)
8690Sstevel@tonic-gate 		(void) pthread_cond_wait(&lp->log_cv, &lp->log_lock);
8700Sstevel@tonic-gate 
8710Sstevel@tonic-gate 	if (lp->log_off == lp->log_beg) {
8720Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&lp->log_lock);
8730Sstevel@tonic-gate 		return; /* no records appended yet */
8740Sstevel@tonic-gate 	}
8750Sstevel@tonic-gate 
8760Sstevel@tonic-gate 	lp->log_flags |= FMD_LF_BUSY;
8770Sstevel@tonic-gate 	skp = lp->log_skip;
8780Sstevel@tonic-gate 	ea_clear(&lp->log_ea); /* resync exacct file */
8790Sstevel@tonic-gate 
8800Sstevel@tonic-gate 	/*
8810Sstevel@tonic-gate 	 * If FMD_LF_REPLAY is set, begin our replay at either log_skip (if it
8820Sstevel@tonic-gate 	 * is non-zero) or at log_beg.  Otherwise replay from the end (log_off)
8830Sstevel@tonic-gate 	 */
8840Sstevel@tonic-gate 	if (lp->log_flags & FMD_LF_REPLAY) {
8850Sstevel@tonic-gate 		off = MAX(lp->log_beg, lp->log_skip);
8860Sstevel@tonic-gate 		c = CAT_FMA_RGROUP;
8870Sstevel@tonic-gate 	} else {
8880Sstevel@tonic-gate 		off = lp->log_off;
8890Sstevel@tonic-gate 		c = CAT_FMA_GROUP;
8900Sstevel@tonic-gate 	}
8910Sstevel@tonic-gate 
8920Sstevel@tonic-gate 	if (lseek64(lp->log_fd, off, SEEK_SET) != off) {
8930Sstevel@tonic-gate 		fmd_panic("failed to seek %s to 0x%llx\n",
8940Sstevel@tonic-gate 		    lp->log_name, (u_longlong_t)off);
8950Sstevel@tonic-gate 	}
8960Sstevel@tonic-gate 
8970Sstevel@tonic-gate 	/*
8980Sstevel@tonic-gate 	 * If FMD_LF_REPLAY is not set, back up to the start of the previous
8990Sstevel@tonic-gate 	 * object and make sure this object is an EO_GROUP; otherwise return.
9000Sstevel@tonic-gate 	 */
9010Sstevel@tonic-gate 	if (!(lp->log_flags & FMD_LF_REPLAY) &&
9020Sstevel@tonic-gate 	    (type = ea_previous_object(&lp->log_ea, &obj)) != EO_GROUP) {
9030Sstevel@tonic-gate 		fmd_error(EFMD_LOG_REPLAY, "last log object is of unexpected "
9040Sstevel@tonic-gate 		    "type %d (log may be truncated or corrupt)\n", type);
9050Sstevel@tonic-gate 		goto out;
9060Sstevel@tonic-gate 	}
9070Sstevel@tonic-gate 
9080Sstevel@tonic-gate 	while ((grp = ea_get_object_tree(&lp->log_ea, 1)) != NULL) {
9090Sstevel@tonic-gate 		if (!(lp->log_flags & FMD_LF_REPLAY))
9100Sstevel@tonic-gate 			off -= ea_pack_object(grp, NULL, 0);
9110Sstevel@tonic-gate 		else if (n == 0 && grp->eo_catalog == CAT_FMA_GROUP)
9120Sstevel@tonic-gate 			skp = off; /* update skip */
9130Sstevel@tonic-gate 
9140Sstevel@tonic-gate 		/*
9150Sstevel@tonic-gate 		 * We temporarily drop log_lock around the call to unpack the
9160Sstevel@tonic-gate 		 * event, hold it, and perform the callback, because these
9170Sstevel@tonic-gate 		 * operations may try to acquire log_lock to bump log_refs.
9180Sstevel@tonic-gate 		 * We cannot lose control because the FMD_LF_BUSY flag is set.
9190Sstevel@tonic-gate 		 */
9200Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&lp->log_lock);
9210Sstevel@tonic-gate 
9220Sstevel@tonic-gate 		if (grp->eo_catalog == c &&
9230Sstevel@tonic-gate 		    (ep = fmd_log_unpack(lp, grp, off)) != NULL) {
9240Sstevel@tonic-gate 
9250Sstevel@tonic-gate 			TRACE((FMD_DBG_LOG, "replay %s %p off %llx",
9260Sstevel@tonic-gate 			    lp->log_tag, (void *)ep, (u_longlong_t)off));
9270Sstevel@tonic-gate 
9280Sstevel@tonic-gate 			fmd_event_hold(ep);
9290Sstevel@tonic-gate 			func(lp, ep, data);
9300Sstevel@tonic-gate 			fmd_event_rele(ep);
9310Sstevel@tonic-gate 			n++;
9320Sstevel@tonic-gate 		}
9330Sstevel@tonic-gate 
9340Sstevel@tonic-gate 		(void) pthread_mutex_lock(&lp->log_lock);
9350Sstevel@tonic-gate 		off += ea_pack_object(grp, NULL, 0);
9360Sstevel@tonic-gate 		ea_free_object(grp, EUP_ALLOC);
9370Sstevel@tonic-gate 	}
9380Sstevel@tonic-gate 
9390Sstevel@tonic-gate 	if (ea_error() != EXR_EOF) {
9400Sstevel@tonic-gate 		fmd_error(EFMD_LOG_REPLAY, "failed to replay %s event at "
9410Sstevel@tonic-gate 		    "offset 0x%llx: %s\n", lp->log_name, (u_longlong_t)off,
9420Sstevel@tonic-gate 		    fmd_ea_strerror(ea_error()));
9430Sstevel@tonic-gate 	}
9440Sstevel@tonic-gate 
9450Sstevel@tonic-gate 	if (n == 0)
9460Sstevel@tonic-gate 		skp = off; /* if no replays, move skip to where we ended up */
9470Sstevel@tonic-gate 
9480Sstevel@tonic-gate out:
9490Sstevel@tonic-gate 	if (lseek64(lp->log_fd, lp->log_off, SEEK_SET) != lp->log_off) {
9500Sstevel@tonic-gate 		fmd_panic("failed to seek %s to 0x%llx\n",
9510Sstevel@tonic-gate 		    lp->log_name, (u_longlong_t)lp->log_off);
9520Sstevel@tonic-gate 	}
9530Sstevel@tonic-gate 
9540Sstevel@tonic-gate 	if (skp != lp->log_skip) {
9550Sstevel@tonic-gate 		lp->log_flags |= FMD_LF_DIRTY;
9560Sstevel@tonic-gate 		lp->log_skip = skp;
9570Sstevel@tonic-gate 	}
9580Sstevel@tonic-gate 
9590Sstevel@tonic-gate 	lp->log_flags &= ~FMD_LF_BUSY;
9601193Smws 	(void) pthread_cond_broadcast(&lp->log_cv);
9610Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&lp->log_lock);
9620Sstevel@tonic-gate }
9630Sstevel@tonic-gate 
9640Sstevel@tonic-gate void
fmd_log_update(fmd_log_t * lp)9650Sstevel@tonic-gate fmd_log_update(fmd_log_t *lp)
9660Sstevel@tonic-gate {
9670Sstevel@tonic-gate 	ea_object_t toc, item;
9680Sstevel@tonic-gate 	off64_t skip = 0;
9690Sstevel@tonic-gate 	size_t size;
9700Sstevel@tonic-gate 	void *buf;
9710Sstevel@tonic-gate 
9720Sstevel@tonic-gate 	(void) pthread_mutex_lock(&lp->log_lock);
9730Sstevel@tonic-gate 
9740Sstevel@tonic-gate 	if (lp->log_flags & FMD_LF_DIRTY) {
9750Sstevel@tonic-gate 		lp->log_flags &= ~FMD_LF_DIRTY;
9760Sstevel@tonic-gate 		skip = lp->log_skip;
9770Sstevel@tonic-gate 	}
9780Sstevel@tonic-gate 
9790Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&lp->log_lock);
9800Sstevel@tonic-gate 
9810Sstevel@tonic-gate 	/*
9820Sstevel@tonic-gate 	 * If the skip needs to be updated, construct a TOC record group
9830Sstevel@tonic-gate 	 * containing the skip offset and overwrite the TOC in-place.
9840Sstevel@tonic-gate 	 */
9850Sstevel@tonic-gate 	if (skip != 0 && ea_set_group(&toc, CAT_FMA_GROUP) == 0 &&
9860Sstevel@tonic-gate 	    ea_set_item(&item, CAT_FMA_OFFSET, &skip, 0) == 0) {
9870Sstevel@tonic-gate 
9880Sstevel@tonic-gate 		(void) ea_attach_to_group(&toc, &item);
9890Sstevel@tonic-gate 		size = ea_pack_object(&toc, NULL, 0);
9900Sstevel@tonic-gate 		buf = fmd_alloc(size, FMD_SLEEP);
9910Sstevel@tonic-gate 
9920Sstevel@tonic-gate 		(void) ea_pack_object(&toc, buf, size);
9930Sstevel@tonic-gate 		ASSERT(lp->log_toc + size == lp->log_beg);
9940Sstevel@tonic-gate 
9950Sstevel@tonic-gate 		if (pwrite64(lp->log_fd, buf, size, lp->log_toc) == size) {
9960Sstevel@tonic-gate 			TRACE((FMD_DBG_LOG, "updated skip to %llx", skip));
9970Sstevel@tonic-gate 		} else {
9980Sstevel@tonic-gate 			fmd_error(EFMD_LOG_UPDATE,
9990Sstevel@tonic-gate 			    "failed to log_update %s", lp->log_tag);
10000Sstevel@tonic-gate 		}
10010Sstevel@tonic-gate 
10020Sstevel@tonic-gate 		fmd_free(buf, size);
10030Sstevel@tonic-gate 		(void) ea_free_item(&item, EUP_ALLOC);
10040Sstevel@tonic-gate 	}
10050Sstevel@tonic-gate }
10060Sstevel@tonic-gate 
10070Sstevel@tonic-gate /*
10080Sstevel@tonic-gate  * Rotate the specified log by renaming its underlying file to a staging file
10090Sstevel@tonic-gate  * that can be handed off to logadm(1M) or an administrator script.  If the
10100Sstevel@tonic-gate  * rename succeeds, open a new log file using the old path and return it.
10110Sstevel@tonic-gate  * Note that we are relying our caller to use some higher-level mechanism to
10120Sstevel@tonic-gate  * ensure that fmd_log_rotate() cannot be called while other threads are
10130Sstevel@tonic-gate  * attempting fmd_log_append() using the same log (fmd's d_log_lock is used
10140Sstevel@tonic-gate  * for the global errlog and fltlog).
10150Sstevel@tonic-gate  */
10160Sstevel@tonic-gate fmd_log_t *
fmd_log_rotate(fmd_log_t * lp)10170Sstevel@tonic-gate fmd_log_rotate(fmd_log_t *lp)
10180Sstevel@tonic-gate {
10190Sstevel@tonic-gate 	char npath[PATH_MAX];
10200Sstevel@tonic-gate 	fmd_log_t *nlp;
10210Sstevel@tonic-gate 
1022*3323Scindi 	(void) snprintf(npath, sizeof (npath), "%s+", lp->log_name);
1023*3323Scindi 
1024*3323Scindi 	/*
1025*3323Scindi 	 * Open new log file.
1026*3323Scindi 	 */
1027*3323Scindi 	if ((nlp = fmd_log_open("", npath, lp->log_tag)) == NULL) {
1028*3323Scindi 		fmd_error(EFMD_LOG_ROTATE, "failed to open %s", npath);
1029*3323Scindi 		(void) fmd_set_errno(EFMD_LOG_ROTATE);
1030*3323Scindi 		return (NULL);
1031*3323Scindi 	}
1032*3323Scindi 
10330Sstevel@tonic-gate 	(void) snprintf(npath, sizeof (npath), "%s.0-", lp->log_name);
10340Sstevel@tonic-gate 	(void) pthread_mutex_lock(&lp->log_lock);
10350Sstevel@tonic-gate 
10360Sstevel@tonic-gate 	/*
10370Sstevel@tonic-gate 	 * Check for any pending commits to drain before proceeding.  We can't
10380Sstevel@tonic-gate 	 * rotate the log out if commits are pending because if we die after
10390Sstevel@tonic-gate 	 * the log is moved aside, we won't be able to replay them on restart.
10400Sstevel@tonic-gate 	 */
10410Sstevel@tonic-gate 	if (lp->log_pending != 0) {
10420Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&lp->log_lock);
1043*3323Scindi 		(void) unlink(nlp->log_name);
1044*3323Scindi 		fmd_log_rele(nlp);
10450Sstevel@tonic-gate 		(void) fmd_set_errno(EFMD_LOG_ROTBUSY);
10460Sstevel@tonic-gate 		return (NULL);
10470Sstevel@tonic-gate 	}
10480Sstevel@tonic-gate 
10490Sstevel@tonic-gate 	if (rename(lp->log_name, npath) != 0) {
10500Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&lp->log_lock);
10510Sstevel@tonic-gate 		fmd_error(EFMD_LOG_ROTATE, "failed to rename %s", lp->log_name);
1052*3323Scindi 		(void) unlink(nlp->log_name);
1053*3323Scindi 		fmd_log_rele(nlp);
10540Sstevel@tonic-gate 		(void) fmd_set_errno(EFMD_LOG_ROTATE);
10550Sstevel@tonic-gate 		return (NULL);
10560Sstevel@tonic-gate 	}
10570Sstevel@tonic-gate 
1058*3323Scindi 	if (rename(nlp->log_name, lp->log_name) != 0) {
10590Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&lp->log_lock);
1060*3323Scindi 		fmd_error(EFMD_LOG_ROTATE, "failed to rename %s",
1061*3323Scindi 		    nlp->log_name);
1062*3323Scindi 		(void) unlink(nlp->log_name);
1063*3323Scindi 		fmd_log_rele(nlp);
10640Sstevel@tonic-gate 		(void) fmd_set_errno(EFMD_LOG_ROTATE);
10650Sstevel@tonic-gate 		return (NULL);
10660Sstevel@tonic-gate 	}
10670Sstevel@tonic-gate 
10680Sstevel@tonic-gate 	/*
1069*3323Scindi 	 * Change name of new log file
1070*3323Scindi 	 */
1071*3323Scindi 	fmd_strfree(nlp->log_name);
1072*3323Scindi 	nlp->log_name = fmd_strdup(lp->log_name, FMD_SLEEP);
1073*3323Scindi 
1074*3323Scindi 	/*
10750Sstevel@tonic-gate 	 * If we've rotated the log, no pending events exist so we don't have
10760Sstevel@tonic-gate 	 * any more commits coming, and our caller should have arranged for
10770Sstevel@tonic-gate 	 * no more calls to append.  As such, we can close log_fd for good.
10780Sstevel@tonic-gate 	 */
10790Sstevel@tonic-gate 	if (lp->log_flags & FMD_LF_EAOPEN) {
10800Sstevel@tonic-gate 		(void) ea_close(&lp->log_ea);
10810Sstevel@tonic-gate 		lp->log_flags &= ~FMD_LF_EAOPEN;
10820Sstevel@tonic-gate 	}
10830Sstevel@tonic-gate 
10840Sstevel@tonic-gate 	(void) close(lp->log_fd);
10850Sstevel@tonic-gate 	lp->log_fd = -1;
10860Sstevel@tonic-gate 
10870Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&lp->log_lock);
10880Sstevel@tonic-gate 	return (nlp);
10890Sstevel@tonic-gate }
1090