fmd_log.c revision 7ee93e3bbce920c0d0742deb6632b0939e30b783
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* FMD Log File Subsystem
*
* Events are written to one of two log files as they are received or created;
* the error log tracks all ereport.* events received on the inbound event
* transport, and the fault log tracks all list.* events generated by fmd or
* its client modules. In addition, we use the same log file format to cache
* state and events associated with ASRUs that are named in a diagnosis.
*
* The log files use the exacct format manipulated by libexacct(3LIB) and
* originally defined in PSARC 1999/119. However, the exacct library was
* designed primarily for read-only clients and without the synchronous i/o
* considerations and seeking required for fmd, so we use libexacct here only
* to read and write the file headers and to pack data from memory into a file
* bytestream. All of the i/o and file offset manipulations are performed by
* the fmd code below. Our exacct file management uses the following grammar:
*
* file := hdr toc event*
* hdr := EXD_FMA_LABEL EXD_FMA_VERSION EXD_FMA_OSREL EXD_FMA_OSVER
* EXD_FMA_PLAT EXD_FMA_UUID
* toc := EXD_FMA_OFFSET
* event := EXD_FMA_TODSEC EXD_FMA_TODNSEC EXD_FMA_NVLIST evref* or legacy evref
* evref := EXD_FMA_UUID EXD_FMA_OFFSET
* legacy evref := EXD_FMA_MAJOR EXD_FMA_MINOR EXD_FMA_INODE EXD_FMA_OFFSET
*
* Any event can be uniquely identified by the tuple (file, offset) where file
* is encoded as (uuid) when we are cross-linking files. For legacy file
* formats we still support encoding the reference as (major, minor, inode).
* Note that we break out of the file's dev_t into its two 32-bit components to
* permit development of either 32-bit or 64-bit log readers and writers; the
* LFS APIs do not yet export a 64-bit dev_t to fstat64(), so there is no way
* for a 32-bit application to retrieve and store a 64-bit dev_t.
*
* In order to replay events in the event of an fmd crash, events are initially
* written to the error log using the group catalog tag EXD_GROUP_RFMA by the
* fmd_log_append() function. Later, once an event transitions from the
* received state to one of its other states (see fmd_event.c for details),
* fmd_log_commit() is used to overwrite the tag with EXD_GROUP_FMA, indicating
* that the event is fully processed and no longer needs to be replayed.
*/
#include <sys/exacct_impl.h>
#include <unistd.h>
#include <limits.h>
#include <fcntl.h>
#include <ctype.h>
#include <fmd_alloc.h>
#include <fmd_error.h>
#include <fmd_string.h>
#include <fmd_event.h>
#include <fmd_conf.h>
#include <fmd_subr.h>
#include <fmd_case.h>
#include <fmd_log.h>
#include <fmd.h>
static ssize_t
{
while (resid != 0) {
break;
}
if (resid == n && n != 0)
return (-1);
return (n - resid);
}
static int
{
int err = 0;
if (err == 0) {
} else
}
static int
{
return (fmd_set_errno(err));
}
static int
{
int got_version = 0, got_label = 0;
const char *p;
/*
* Read the first group of log meta-data: the write-once read-only
* file header. We read all records in this group, ignoring all but
* the VERSION and LABEL, which are required and must be verified.
*/
"invalid fma hdr record group"));
}
switch (obj->eo_catalog) {
case CAT_FMA_VERSION:
*p != '\0'; p++) {
if (isdigit(*p))
else
break;
}
*p != '\0'; p++) {
if (isdigit(*p))
else
break;
}
"%s is not supported by this daemon\n",
return (fmd_set_errno(EFMD_LOG_VERSION));
}
got_version++;
break;
case CAT_FMA_LABEL:
"does not matched expected tag '%s'\n",
return (fmd_set_errno(EFMD_LOG_INVAL));
}
got_label++;
break;
case CAT_FMA_UUID:
break;
}
}
if (!got_version || !got_label) {
return (fmd_set_errno(EFMD_LOG_INVAL));
}
/*
* Read the second group of log meta-data: the table of contents. We
* expect this group to contain an OFFSET object indicating the current
* value of log_skip. We save this in our fmd_log_t and then return.
*/
"invalid fma toc record group"));
}
}
return (0);
}
static int
{
const char *creator;
return (fmd_set_errno(EFMD_LOG_EXACCT));
}
return (0);
}
static fmd_log_t *
{
int err;
top:
return (NULL);
}
/*
* If our open() created the log file, use libexacct to write a header
* and position the file just after the header (EO_TAIL). If the log
* file already existed, use libexacct to validate the header and again
* position the file just after the header (EO_HEAD). Note that we lie
* to libexacct about 'oflags' in order to achieve the desired result.
*/
} else {
}
/*
* If ea_fdopen() failed and the log was pre-existing, attempt to move
* it aside and start a new one. If we created the log but failed to
* initialize it, then we have no choice but to give up (e.g. EROFS).
*/
if (err) {
}
goto top;
}
return (NULL);
}
return (lp);
}
{
}
{
}
void
{
}
}
}
void
{
lp->log_pending++;
}
}
void
{
}
void
{
else
}
void
{
int err = 0;
else
if (err != 0) {
goto exerr;
}
/*
* If this event has a case associated with it (i.e. it is a list),
* then allocate a block of ea_object_t's and fill in a group for
* each event saved in the case's item list. For each such group,
* we attach it to grp1, which in turn will be attached to grp0.
*/
continue; /* event was never logged */
/*
* If the event log file is in legacy format,
* then write the xref to the file in the legacy
* file uuid.
*/
} else {
goto exerrcp;
}
}
}
}
/*
* Before writing the record, check to see if this would cause the free
* space in the filesystem to drop below our minfree threshold. If so,
* don't bother attempting the write and instead pretend it failed. As
* fmd(1M) runs as root, it will be able to access the space "reserved"
* for root, and therefore can run the system of out of disk space in a
* heavy error load situation, violating the basic design principle of
* fmd(1M) that we don't want to make a bad situation even worse.
*/
lp->log_pending++;
}
} else {
/*
* If we can't write append the record, seek the file back to
* the original location and truncate it there in order to make
* sure the file is always in a sane state w.r.t. libexacct.
*/
}
int i = 0;
}
}
/*
* Keep track of out-of-space errors using global statistics. As we're
* out of disk space, it's unlikely the EFMD_LOG_APPEND will be logged.
*/
fmd_stat_t *sp;
else
}
if (err != 0) {
}
}
/*
* Commit an event to the log permanently, indicating that it should not be
* replayed on restart. This is done by overwriting the event group's catalog
* code with EXD_GROUP_FMA (from EXD_GROUP_RFMA used in fmd_log_append()). We
* use pwrite64() to update the existing word directly, using somewhat guilty
* knowledge that exacct stores the 32-bit catalog word first for each object.
* Since we are overwriting an existing log location using pwrite64() and hold
* the event lock, we do not need to hold the log_lock during the i/o.
*/
void
{
ea_catalog_t c;
int err = 0;
return; /* log does not require replay tagging */
c = CAT_FMA_GROUP;
exacct_order32(&c);
/*
* If we have committed the event, check to see if the TOC skip
* offset needs to be updated, and decrement the pending count.
*/
}
lp->log_pending--;
} else {
}
}
/*
* If we need to destroy an event and it wasn't able to be committed, we permit
* the owner to decommit from ever trying again. This operation decrements the
* pending count on the log and broadcasts to anyone waiting on log_cv.
*/
void
{
return; /* log does not require replay tagging */
lp->log_pending--;
}
static fmd_event_t *
{
char *class;
int err;
switch (obj->eo_catalog) {
case CAT_FMA_NVLIST:
return (NULL);
}
break;
case CAT_FMA_TODSEC:
break;
case CAT_FMA_TODNSEC:
break;
}
}
"required object(s) missing from record group\n");
return (NULL);
}
"record is missing required '%s' nvpair\n", FM_CLASS);
return (NULL);
}
return (fmd_event_recreate(FMD_EVT_PROTOCOL,
}
/*
* Replay event(s) from the specified log by invoking the specified callback
* function 'func' for each event. If the log has the FMD_LF_REPLAY flag set,
* we replay all events after log_skip that have the FMA_RGROUP group tag.
* This mode is used for the error telemetry log. If the log does not have
* this flag set (used for ASRU logs), only the most recent event is replayed.
*/
void
{
ea_catalog_t c;
uint_t n = 0;
return; /* we just created this log: never replay events */
}
return; /* no records appended yet */
}
/*
* If FMD_LF_REPLAY is set, begin our replay at either log_skip (if it
* is non-zero) or at log_beg. Otherwise replay from the end (log_off)
*/
c = CAT_FMA_RGROUP;
} else {
c = CAT_FMA_GROUP;
}
fmd_panic("failed to seek %s to 0x%llx\n",
}
/*
* If FMD_LF_REPLAY is not set, back up to the start of the previous
* object and make sure this object is an EO_GROUP; otherwise return.
*/
"type %d (log may be truncated or corrupt)\n", type);
goto out;
}
/*
* We temporarily drop log_lock around the call to unpack the
* event, hold it, and perform the callback, because these
* operations may try to acquire log_lock to bump log_refs.
* We cannot lose control because the FMD_LF_BUSY flag is set.
*/
if (grp->eo_catalog == c &&
n++;
}
}
fmd_ea_strerror(ea_error()));
}
if (n == 0)
out:
fmd_panic("failed to seek %s to 0x%llx\n",
}
}
}
void
{
void *buf;
}
/*
* If the skip needs to be updated, construct a TOC record group
* containing the skip offset and overwrite the TOC in-place.
*/
} else {
}
}
}
/*
* Rotate the specified log by renaming its underlying file to a staging file
* that can be handed off to logadm(1M) or an administrator script. If the
* rename succeeds, open a new log file using the old path and return it.
* Note that we are relying our caller to use some higher-level mechanism to
* ensure that fmd_log_rotate() cannot be called while other threads are
* attempting fmd_log_append() using the same log (fmd's d_log_lock is used
* for the global errlog and fltlog).
*/
{
/*
* Check for any pending commits to drain before proceeding. We can't
* rotate the log out if commits are pending because if we die after
* the log is moved aside, we won't be able to replay them on restart.
*/
if (lp->log_pending != 0) {
(void) fmd_set_errno(EFMD_LOG_ROTBUSY);
return (NULL);
}
(void) fmd_set_errno(EFMD_LOG_ROTATE);
return (NULL);
}
(void) fmd_set_errno(EFMD_LOG_ROTATE);
return (NULL);
}
/*
* If we've rotated the log, no pending events exist so we don't have
* any more commits coming, and our caller should have arranged for
* no more calls to append. As such, we can close log_fd for good.
*/
}
return (nlp);
}