zfsd(8), the ZFS fault management daemon
Add zfsd, which deals with hard drive faults in ZFS pools. It manages hotspares and replements in drive slots that publish physical paths. cddl/usr.sbin/zfsd Add zfsd(8) and its unit tests cddl/usr.sbin/Makefile Add zfsd to the build lib/libdevdctl A C++ library that helps devd clients process events lib/Makefile share/mk/bsd.libnames.mk share/mk/src.libnames.mk Add libdevdctl to the build. It's a private library, unusable by out-of-tree software. etc/defaults/rc.conf By default, set zfsd_enable to NO etc/mtree/BSD.include.dist Add a directory for libdevdctl's include files etc/mtree/BSD.tests.dist Add a directory for zfsd's unit tests etc/mtree/BSD.var.dist Add /var/db/zfsd/cases, where zfsd stores case files while it's shut down. etc/rc.d/Makefile etc/rc.d/zfsd Add zfsd's rc script sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev.c Fix the resource.fs.zfs.statechange message. It had a number of problems: It was only being emitted on a transition to the HEALTHY state. That made it impossible for zfsd to take actions based on drives getting sicker. It compared the new state to vdev_prevstate, which is the state that the vdev had the last time it was opened. That doesn't make sense, because a vdev can change state multiple times without being reopened. vdev_set_state contains logic that will change the device's new state based on various conditions. However, the statechange event was being posted _before_ that logic took effect. Now it's being posted after. Submitted by: gibbs, asomers, mav, allanjude Reviewed by: mav, delphij Relnotes: yes Sponsored by: Spectra Logic Corp, iX Systems Differential Revision: https://reviews.freebsd.org/D6564
This commit is contained in:
parent
da1a7cbaba
commit
442baa5184
@ -7,6 +7,7 @@ SUBDIR= ${_dtrace} \
|
||||
${_plockstat} \
|
||||
${_tests} \
|
||||
${_zdb} \
|
||||
${_zfsd} \
|
||||
${_zhack}
|
||||
|
||||
.if ${MK_TESTS} != "no"
|
||||
@ -18,6 +19,9 @@ _tests= tests
|
||||
_zdb= zdb
|
||||
_zhack= zhack
|
||||
.endif
|
||||
. if ${MK_CXX} != "no"
|
||||
_zfsd= zfsd
|
||||
. endif
|
||||
.endif
|
||||
|
||||
.if ${MACHINE_ARCH} == "amd64" || ${MACHINE_ARCH} == "i386"
|
||||
|
13
cddl/usr.sbin/zfsd/Makefile
Normal file
13
cddl/usr.sbin/zfsd/Makefile
Normal file
@ -0,0 +1,13 @@
|
||||
# $FreeBSD$
|
||||
|
||||
SRCDIR=${.CURDIR}/../../..
|
||||
.include "Makefile.common"
|
||||
|
||||
PROG_CXX= zfsd
|
||||
MAN= zfsd.8
|
||||
|
||||
.include <bsd.prog.mk>
|
||||
|
||||
# The unittests require devel/googletest and devel/googlemock from ports.
|
||||
# Don't automatically build them.
|
||||
SUBDIR=
|
42
cddl/usr.sbin/zfsd/Makefile.common
Normal file
42
cddl/usr.sbin/zfsd/Makefile.common
Normal file
@ -0,0 +1,42 @@
|
||||
# $FreeBSD$
|
||||
|
||||
SRCS= callout.cc \
|
||||
case_file.cc \
|
||||
zfsd_event.cc \
|
||||
vdev.cc \
|
||||
vdev_iterator.cc \
|
||||
zfsd.cc \
|
||||
zfsd_exception.cc \
|
||||
zpool_list.cc \
|
||||
zfsd_main.cc
|
||||
|
||||
WARNS?= 3
|
||||
|
||||
# Ignore warnings about Solaris specific pragmas.
|
||||
IGNORE_PRAGMA= YES
|
||||
|
||||
INCFLAGS+= -I${SRCDIR}/cddl/contrib/opensolaris/lib/libzpool/common
|
||||
INCFLAGS+= -I${SRCDIR}/cddl/compat/opensolaris/include
|
||||
INCFLAGS+= -I${SRCDIR}/cddl/compat/opensolaris/lib/libumem
|
||||
INCFLAGS+= -I${SRCDIR}/sys/cddl/compat/opensolaris
|
||||
INCFLAGS+= -I${SRCDIR}/cddl/contrib/opensolaris/head
|
||||
INCFLAGS+= -I${SRCDIR}/cddl/contrib/opensolaris/lib/libuutil/common
|
||||
INCFLAGS+= -I${SRCDIR}/cddl/contrib/opensolaris/lib/libumem/common
|
||||
INCFLAGS+= -I${SRCDIR}/cddl/contrib/opensolaris/lib/libzfs_core/common
|
||||
INCFLAGS+= -I${SRCDIR}/cddl/contrib/opensolaris/lib/libzfs/common
|
||||
INCFLAGS+= -I${SRCDIR}/cddl/contrib/opensolaris/lib/libnvpair
|
||||
INCFLAGS+= -I${SRCDIR}/sys/cddl/contrib/opensolaris/common/zfs
|
||||
INCFLAGS+= -I${SRCDIR}/sys/cddl/contrib/opensolaris/uts/common
|
||||
INCFLAGS+= -I${SRCDIR}/sys/cddl/contrib/opensolaris/uts/common/fs/zfs
|
||||
INCFLAGS+= -I${SRCDIR}/sys/cddl/contrib/opensolaris/uts/common/sys
|
||||
|
||||
CFLAGS= -g -DNEED_SOLARIS_BOOLEAN ${INCFLAGS}
|
||||
|
||||
DPADD= ${LIBDEVDCTL} ${LIBZFS} ${LIBZFS_CORE} ${LIBUTIL} ${LIBGEOM} \
|
||||
${LIBBSDXML} ${LIBSBUF} ${LIBNVPAIR} ${LIBUUTIL}
|
||||
LIBADD= devdctl zfs zfs_core util geom bsdxml sbuf nvpair uutil
|
||||
|
||||
cscope:
|
||||
find ${.CURDIR} -type f -a \( -name "*.[ch]" -o -name "*.cc" \) \
|
||||
> ${.CURDIR}/cscope.files
|
||||
cd ${.CURDIR} && cscope -buq ${INCFLAGS}
|
219
cddl/usr.sbin/zfsd/callout.cc
Normal file
219
cddl/usr.sbin/zfsd/callout.cc
Normal file
@ -0,0 +1,219 @@
|
||||
/*-
|
||||
* Copyright (c) 2011, 2012, 2013 Spectra Logic Corporation
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions, and the following disclaimer,
|
||||
* without modification.
|
||||
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
|
||||
* substantially similar to the "NO WARRANTY" disclaimer below
|
||||
* ("Disclaimer") and any redistribution must be conditioned upon
|
||||
* including a substantially similar Disclaimer requirement for further
|
||||
* binary redistribution.
|
||||
*
|
||||
* NO WARRANTY
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGES.
|
||||
*
|
||||
* Authors: Justin T. Gibbs (Spectra Logic Corporation)
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file callout.cc
|
||||
*
|
||||
* \brief Implementation of the Callout class - multi-client
|
||||
* timer services built on top of the POSIX interval timer.
|
||||
*/
|
||||
|
||||
#include <sys/time.h>
|
||||
|
||||
#include <signal.h>
|
||||
#include <syslog.h>
|
||||
|
||||
#include <climits>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include <devdctl/guid.h>
|
||||
#include <devdctl/event.h>
|
||||
#include <devdctl/event_factory.h>
|
||||
#include <devdctl/consumer.h>
|
||||
#include <devdctl/exception.h>
|
||||
|
||||
#include "callout.h"
|
||||
#include "vdev_iterator.h"
|
||||
#include "zfsd.h"
|
||||
#include "zfsd_exception.h"
|
||||
|
||||
std::list<Callout *> Callout::s_activeCallouts;
|
||||
bool Callout::s_alarmFired(false);
|
||||
|
||||
void
|
||||
Callout::Init()
|
||||
{
|
||||
signal(SIGALRM, Callout::AlarmSignalHandler);
|
||||
}
|
||||
|
||||
bool
|
||||
Callout::Stop()
|
||||
{
|
||||
if (!IsPending())
|
||||
return (false);
|
||||
|
||||
for (std::list<Callout *>::iterator it(s_activeCallouts.begin());
|
||||
it != s_activeCallouts.end(); it++) {
|
||||
if (*it != this)
|
||||
continue;
|
||||
|
||||
it = s_activeCallouts.erase(it);
|
||||
if (it != s_activeCallouts.end()) {
|
||||
|
||||
/*
|
||||
* Maintain correct interval for the
|
||||
* callouts that follow the just removed
|
||||
* entry.
|
||||
*/
|
||||
timeradd(&(*it)->m_interval, &m_interval,
|
||||
&(*it)->m_interval);
|
||||
}
|
||||
break;
|
||||
}
|
||||
m_pending = false;
|
||||
return (true);
|
||||
}
|
||||
|
||||
bool
|
||||
Callout::Reset(const timeval &interval, CalloutFunc_t *func, void *arg)
|
||||
{
|
||||
bool cancelled(false);
|
||||
|
||||
if (!timerisset(&interval))
|
||||
throw ZfsdException("Callout::Reset: interval of 0");
|
||||
|
||||
cancelled = Stop();
|
||||
|
||||
m_interval = interval;
|
||||
m_func = func;
|
||||
m_arg = arg;
|
||||
m_pending = true;
|
||||
|
||||
std::list<Callout *>::iterator it(s_activeCallouts.begin());
|
||||
for (; it != s_activeCallouts.end(); it++) {
|
||||
|
||||
if (timercmp(&(*it)->m_interval, &m_interval, <=)) {
|
||||
/*
|
||||
* Decrease our interval by those that come
|
||||
* before us.
|
||||
*/
|
||||
timersub(&m_interval, &(*it)->m_interval, &m_interval);
|
||||
} else {
|
||||
/*
|
||||
* Account for the time between the newly
|
||||
* inserted event and those that follow.
|
||||
*/
|
||||
timersub(&(*it)->m_interval, &m_interval,
|
||||
&(*it)->m_interval);
|
||||
break;
|
||||
}
|
||||
}
|
||||
s_activeCallouts.insert(it, this);
|
||||
|
||||
|
||||
if (s_activeCallouts.front() == this) {
|
||||
itimerval timerval = { {0, 0}, m_interval };
|
||||
|
||||
setitimer(ITIMER_REAL, &timerval, NULL);
|
||||
}
|
||||
|
||||
return (cancelled);
|
||||
}
|
||||
|
||||
void
|
||||
Callout::AlarmSignalHandler(int)
|
||||
{
|
||||
s_alarmFired = true;
|
||||
ZfsDaemon::WakeEventLoop();
|
||||
}
|
||||
|
||||
void
|
||||
Callout::ExpireCallouts()
|
||||
{
|
||||
if (!s_alarmFired)
|
||||
return;
|
||||
|
||||
s_alarmFired = false;
|
||||
if (s_activeCallouts.empty()) {
|
||||
/* Callout removal/SIGALRM race was lost. */
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Expire the first callout (the one we used to set the
|
||||
* interval timer) as well as any callouts following that
|
||||
* expire at the same time (have a zero interval from
|
||||
* the callout before it).
|
||||
*/
|
||||
do {
|
||||
Callout *cur(s_activeCallouts.front());
|
||||
s_activeCallouts.pop_front();
|
||||
cur->m_pending = false;
|
||||
cur->m_func(cur->m_arg);
|
||||
} while (!s_activeCallouts.empty()
|
||||
&& timerisset(&s_activeCallouts.front()->m_interval) == 0);
|
||||
|
||||
if (!s_activeCallouts.empty()) {
|
||||
Callout *next(s_activeCallouts.front());
|
||||
itimerval timerval = { { 0, 0 }, next->m_interval };
|
||||
|
||||
setitimer(ITIMER_REAL, &timerval, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
timeval
|
||||
Callout::TimeRemaining() const
|
||||
{
|
||||
/*
|
||||
* Outline: Add the m_interval for each callout in s_activeCallouts
|
||||
* ahead of this, except for the first callout. Add to that the result
|
||||
* of getitimer (That's because the first callout stores its original
|
||||
* interval setting while the timer is ticking).
|
||||
*/
|
||||
itimerval timervalToAlarm;
|
||||
timeval timeToExpiry;
|
||||
std::list<Callout *>::iterator it;
|
||||
|
||||
if (!IsPending()) {
|
||||
timeToExpiry.tv_sec = INT_MAX;
|
||||
timeToExpiry.tv_usec = 999999; /*maximum normalized value*/
|
||||
return (timeToExpiry);
|
||||
}
|
||||
|
||||
timerclear(&timeToExpiry);
|
||||
getitimer(ITIMER_REAL, &timervalToAlarm);
|
||||
timeval& timeToAlarm = timervalToAlarm.it_value;
|
||||
timeradd(&timeToExpiry, &timeToAlarm, &timeToExpiry);
|
||||
|
||||
it =s_activeCallouts.begin();
|
||||
it++; /*skip the first callout in the list*/
|
||||
for (; it != s_activeCallouts.end(); it++) {
|
||||
timeradd(&timeToExpiry, &(*it)->m_interval, &timeToExpiry);
|
||||
if ((*it) == this)
|
||||
break;
|
||||
}
|
||||
return (timeToExpiry);
|
||||
}
|
185
cddl/usr.sbin/zfsd/callout.h
Normal file
185
cddl/usr.sbin/zfsd/callout.h
Normal file
@ -0,0 +1,185 @@
|
||||
/*-
|
||||
* Copyright (c) 2011, 2012, 2013 Spectra Logic Corporation
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions, and the following disclaimer,
|
||||
* without modification.
|
||||
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
|
||||
* substantially similar to the "NO WARRANTY" disclaimer below
|
||||
* ("Disclaimer") and any redistribution must be conditioned upon
|
||||
* including a substantially similar Disclaimer requirement for further
|
||||
* binary redistribution.
|
||||
*
|
||||
* NO WARRANTY
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGES.
|
||||
*
|
||||
* Authors: Justin T. Gibbs (Spectra Logic Corporation)
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file callout.h
|
||||
*
|
||||
* \brief Interface for timer based callback services.
|
||||
*
|
||||
* Header requirements:
|
||||
*
|
||||
* #include <sys/time.h>
|
||||
*
|
||||
* #include <list>
|
||||
*/
|
||||
|
||||
#ifndef _CALLOUT_H_
|
||||
#define _CALLOUT_H_
|
||||
|
||||
/**
|
||||
* \brief Type of the function callback from a Callout.
|
||||
*/
|
||||
typedef void CalloutFunc_t(void *);
|
||||
|
||||
/**
|
||||
* \brief Interface to a schedulable one-shot timer with the granularity
|
||||
* of the system clock (see setitimer(2)).
|
||||
*
|
||||
* Determination of callback expiration is triggered by the SIGALRM
|
||||
* signal. Callout callbacks are always delivered from Zfsd's event
|
||||
* processing loop.
|
||||
*
|
||||
* Periodic actions can be triggered via the Callout mechanisms by
|
||||
* resetting the Callout from within its callback.
|
||||
*/
|
||||
class Callout
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* Initialize the Callout subsystem.
|
||||
*/
|
||||
static void Init();
|
||||
|
||||
/**
|
||||
* Function called (via SIGALRM) when our interval
|
||||
* timer expires.
|
||||
*/
|
||||
static void AlarmSignalHandler(int);
|
||||
|
||||
/**
|
||||
* Execute callbacks for all callouts that have the same
|
||||
* expiration time as the first callout in the list.
|
||||
*/
|
||||
static void ExpireCallouts();
|
||||
|
||||
/** Constructor. */
|
||||
Callout();
|
||||
|
||||
/**
|
||||
* Returns true if callout has not been stopped,
|
||||
* or deactivated since the last time the callout was
|
||||
* reset.
|
||||
*/
|
||||
bool IsActive() const;
|
||||
|
||||
/**
|
||||
* Returns true if callout is still waiting to expire.
|
||||
*/
|
||||
bool IsPending() const;
|
||||
|
||||
/**
|
||||
* Disestablish a callout.
|
||||
*/
|
||||
bool Stop();
|
||||
|
||||
/**
|
||||
* \brief Establish or change a timeout.
|
||||
*
|
||||
* \param interval Timeval indicating the time which must elapse
|
||||
* before this callout fires.
|
||||
* \param func Pointer to the callback funtion
|
||||
* \param arg Argument pointer to pass to callback function
|
||||
*
|
||||
* \return Cancellation status.
|
||||
* true: The previous callback was pending and therefore
|
||||
* was cancelled.
|
||||
* false: The callout was not pending at the time of this
|
||||
* reset request.
|
||||
* In all cases, a new callout is established.
|
||||
*/
|
||||
bool Reset(const timeval &interval, CalloutFunc_t *func, void *arg);
|
||||
|
||||
/**
|
||||
* \brief Calculate the remaining time until this Callout's timer
|
||||
* expires.
|
||||
*
|
||||
* The return value will be slightly greater than the actual time to
|
||||
* expiry.
|
||||
*
|
||||
* If the callout is not pending, returns INT_MAX.
|
||||
*/
|
||||
timeval TimeRemaining() const;
|
||||
|
||||
private:
|
||||
/**
|
||||
* All active callouts sorted by expiration time. The callout
|
||||
* with the nearest expiration time is at the head of the list.
|
||||
*/
|
||||
static std::list<Callout *> s_activeCallouts;
|
||||
|
||||
/**
|
||||
* The interval timer has expired. This variable is set from
|
||||
* signal handler context and tested from Zfsd::EventLoop()
|
||||
* context via ExpireCallouts().
|
||||
*/
|
||||
static bool s_alarmFired;
|
||||
|
||||
/**
|
||||
* Time, relative to others in the active list, until
|
||||
* this callout is fired.
|
||||
*/
|
||||
timeval m_interval;
|
||||
|
||||
/** Callback function argument. */
|
||||
void *m_arg;
|
||||
|
||||
/**
|
||||
* The callback function associated with this timer
|
||||
* entry.
|
||||
*/
|
||||
CalloutFunc_t *m_func;
|
||||
|
||||
/** State of this callout. */
|
||||
bool m_pending;
|
||||
};
|
||||
|
||||
//- Callout public const methods ----------------------------------------------
|
||||
inline bool
|
||||
Callout::IsPending() const
|
||||
{
|
||||
return (m_pending);
|
||||
}
|
||||
|
||||
//- Callout public methods ----------------------------------------------------
|
||||
inline
|
||||
Callout::Callout()
|
||||
: m_arg(0),
|
||||
m_func(NULL),
|
||||
m_pending(false)
|
||||
{
|
||||
timerclear(&m_interval);
|
||||
}
|
||||
|
||||
#endif /* CALLOUT_H_ */
|
1104
cddl/usr.sbin/zfsd/case_file.cc
Normal file
1104
cddl/usr.sbin/zfsd/case_file.cc
Normal file
File diff suppressed because it is too large
Load Diff
426
cddl/usr.sbin/zfsd/case_file.h
Normal file
426
cddl/usr.sbin/zfsd/case_file.h
Normal file
@ -0,0 +1,426 @@
|
||||
/*-
|
||||
* Copyright (c) 2011, 2012, 2013 Spectra Logic Corporation
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions, and the following disclaimer,
|
||||
* without modification.
|
||||
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
|
||||
* substantially similar to the "NO WARRANTY" disclaimer below
|
||||
* ("Disclaimer") and any redistribution must be conditioned upon
|
||||
* including a substantially similar Disclaimer requirement for further
|
||||
* binary redistribution.
|
||||
*
|
||||
* NO WARRANTY
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGES.
|
||||
*
|
||||
* Authors: Justin T. Gibbs (Spectra Logic Corporation)
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file case_file.h
|
||||
*
|
||||
* CaseFile objects aggregate vdev faults that may require ZFSD action
|
||||
* in order to maintain the health of a ZFS pool.
|
||||
*
|
||||
* Header requirements:
|
||||
*
|
||||
* #include <list>
|
||||
*
|
||||
* #include "callout.h"
|
||||
* #include "zfsd_event.h"
|
||||
*/
|
||||
#ifndef _CASE_FILE_H_
|
||||
#define _CASE_FILE_H_
|
||||
|
||||
/*=========================== Forward Declarations ===========================*/
|
||||
class CaseFile;
|
||||
class Vdev;
|
||||
|
||||
/*============================= Class Definitions ============================*/
|
||||
/*------------------------------- CaseFileList -------------------------------*/
|
||||
/**
|
||||
* CaseFileList is a specialization of the standard list STL container.
|
||||
*/
|
||||
typedef std::list< CaseFile *> CaseFileList;
|
||||
|
||||
/*--------------------------------- CaseFile ---------------------------------*/
|
||||
/**
|
||||
* A CaseFile object is instantiated anytime a vdev for an active pool
|
||||
* experiences an I/O error, is faulted by ZFS, or is determined to be
|
||||
* missing/removed.
|
||||
*
|
||||
* A vdev may have at most one CaseFile.
|
||||
*
|
||||
* CaseFiles are retired when a vdev leaves an active pool configuration
|
||||
* or an action is taken to resolve the issues recorded in the CaseFile.
|
||||
*
|
||||
* Logging a case against a vdev does not imply that an immediate action
|
||||
* to resolve a fault is required or even desired. For example, a CaseFile
|
||||
* must accumulate a number of I/O errors in order to flag a device as
|
||||
* degraded.
|
||||
*
|
||||
* Vdev I/O errors are not recorded in ZFS label inforamation. For this
|
||||
* reasons, CaseFile%%s with accumulated I/O error events are serialized
|
||||
* to the file system so that they survive across boots. Currently all
|
||||
* other fault types can be reconstructed from ZFS label information, so
|
||||
* CaseFile%%s for missing, faulted, or degradded members are just recreated
|
||||
* at ZFSD startup instead of being deserialized from the file system.
|
||||
*/
|
||||
class CaseFile
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* \brief Find a CaseFile object by a vdev's pool/vdev GUID tuple.
|
||||
*
|
||||
* \param poolGUID Pool GUID for the vdev of the CaseFile to find.
|
||||
* \param vdevGUID Vdev GUID for the vdev of the CaseFile to find.
|
||||
*
|
||||
* \return If found, a pointer to a valid CaseFile object.
|
||||
* Otherwise NULL.
|
||||
*/
|
||||
static CaseFile *Find(DevdCtl::Guid poolGUID, DevdCtl::Guid vdevGUID);
|
||||
|
||||
/**
|
||||
* \brief Find a CaseFile object by a vdev's current/last known
|
||||
* physical path.
|
||||
*
|
||||
* \param physPath Physical path of the vdev of the CaseFile to find.
|
||||
*
|
||||
* \return If found, a pointer to a valid CaseFile object.
|
||||
* Otherwise NULL.
|
||||
*/
|
||||
static CaseFile *Find(const string &physPath);
|
||||
|
||||
/**
|
||||
* \brief ReEvaluate all open cases whose pool guid matches the argument
|
||||
*
|
||||
* \param poolGUID Only reevaluate cases for this pool
|
||||
* \param event Try to consume this event with the casefile
|
||||
*/
|
||||
static void ReEvaluateByGuid(DevdCtl::Guid poolGUID,
|
||||
const ZfsEvent &event);
|
||||
|
||||
/**
|
||||
* \brief Create or return an existing active CaseFile for the
|
||||
* specified vdev.
|
||||
*
|
||||
* \param vdev The vdev object for which to find/create a CaseFile.
|
||||
*
|
||||
* \return A reference to a valid CaseFile object.
|
||||
*/
|
||||
static CaseFile &Create(Vdev &vdev);
|
||||
|
||||
/**
|
||||
* \brief Deserialize all serialized CaseFile objects found in
|
||||
* the file system.
|
||||
*/
|
||||
static void DeSerialize();
|
||||
|
||||
/**
|
||||
* \brief Emit syslog data on all active CaseFile%%s in the system.
|
||||
*/
|
||||
static void LogAll();
|
||||
|
||||
/**
|
||||
* \brief Destroy the in-core cache of CaseFile data.
|
||||
*
|
||||
* This routine does not disturb the on disk, serialized, CaseFile
|
||||
* data.
|
||||
*/
|
||||
static void PurgeAll();
|
||||
|
||||
DevdCtl::Guid PoolGUID() const;
|
||||
DevdCtl::Guid VdevGUID() const;
|
||||
vdev_state VdevState() const;
|
||||
const string &PoolGUIDString() const;
|
||||
const string &VdevGUIDString() const;
|
||||
const string &PhysicalPath() const;
|
||||
|
||||
/**
|
||||
* \brief Attempt to resolve this CaseFile using the disk
|
||||
* resource at the given device/physical path/vdev object
|
||||
* tuple.
|
||||
*
|
||||
* \param devPath The devfs path for the disk resource.
|
||||
* \param physPath The physical path information reported by
|
||||
* the disk resource.
|
||||
* \param vdev If the disk contains ZFS label information,
|
||||
* a pointer to the disk label's vdev object
|
||||
* data. Otherwise NULL.
|
||||
*
|
||||
* \return True if this event was consumed by this CaseFile.
|
||||
*/
|
||||
bool ReEvaluate(const string &devPath, const string &physPath,
|
||||
Vdev *vdev);
|
||||
|
||||
/**
|
||||
* \brief Update this CaseFile in light of the provided ZfsEvent.
|
||||
*
|
||||
* Must be virtual so it can be overridden in the unit tests
|
||||
*
|
||||
* \param event The ZfsEvent to evaluate.
|
||||
*
|
||||
* \return True if this event was consumed by this CaseFile.
|
||||
*/
|
||||
virtual bool ReEvaluate(const ZfsEvent &event);
|
||||
|
||||
/**
|
||||
* \brief Register an itimer callout for the given event, if necessary
|
||||
*/
|
||||
virtual void RegisterCallout(const DevdCtl::Event &event);
|
||||
|
||||
/**
|
||||
* \brief Close a case if it is no longer relevant.
|
||||
*
|
||||
* This method deals with cases tracking soft errors. Soft errors
|
||||
* will be discarded should a remove event occur within a short period
|
||||
* of the soft errors being reported. We also discard the events
|
||||
* if the vdev is marked degraded or failed.
|
||||
*
|
||||
* \return True if the case is closed. False otherwise.
|
||||
*/
|
||||
bool CloseIfSolved();
|
||||
|
||||
/**
|
||||
* \brief Emit data about this CaseFile via syslog(3).
|
||||
*/
|
||||
void Log();
|
||||
|
||||
/**
|
||||
* \brief Whether we should degrade this vdev
|
||||
*/
|
||||
bool ShouldDegrade() const;
|
||||
|
||||
/**
|
||||
* \brief Whether we should fault this vdev
|
||||
*/
|
||||
bool ShouldFault() const;
|
||||
|
||||
protected:
|
||||
enum {
|
||||
/**
|
||||
* The number of soft errors on a vdev required
|
||||
* to transition a vdev from healthy to degraded
|
||||
* status.
|
||||
*/
|
||||
ZFS_DEGRADE_IO_COUNT = 50
|
||||
};
|
||||
|
||||
static CalloutFunc_t OnGracePeriodEnded;
|
||||
|
||||
/**
|
||||
* \brief scandir(3) filter function used to find files containing
|
||||
* serialized CaseFile data.
|
||||
*
|
||||
* \param dirEntry Directory entry for the file to filter.
|
||||
*
|
||||
* \return Non-zero for a file to include in the selection,
|
||||
* otherwise 0.
|
||||
*/
|
||||
static int DeSerializeSelector(const struct dirent *dirEntry);
|
||||
|
||||
/**
|
||||
* \brief Given the name of a file containing serialized events from a
|
||||
* CaseFile object, create/update an in-core CaseFile object
|
||||
* representing the serialized data.
|
||||
*
|
||||
* \param fileName The name of a file containing serialized events
|
||||
* from a CaseFile object.
|
||||
*/
|
||||
static void DeSerializeFile(const char *fileName);
|
||||
|
||||
/** Constructor. */
|
||||
CaseFile(const Vdev &vdev);
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
* Must be virtual so it can be subclassed in the unit tests
|
||||
*/
|
||||
virtual ~CaseFile();
|
||||
|
||||
/**
|
||||
* \brief Reload state for the vdev associated with this CaseFile.
|
||||
*
|
||||
* \return True if the refresh was successful. False if the system
|
||||
* has no record of the pool or vdev for this CaseFile.
|
||||
*/
|
||||
virtual bool RefreshVdevState();
|
||||
|
||||
/**
|
||||
* \brief Free all events in the m_events list.
|
||||
*/
|
||||
void PurgeEvents();
|
||||
|
||||
/**
|
||||
* \brief Free all events in the m_tentativeEvents list.
|
||||
*/
|
||||
void PurgeTentativeEvents();
|
||||
|
||||
/**
|
||||
* \brief Commit to file system storage.
|
||||
*/
|
||||
void Serialize();
|
||||
|
||||
/**
|
||||
* \brief Retrieve event data from a serialization stream.
|
||||
*
|
||||
* \param caseStream The serializtion stream to parse.
|
||||
*/
|
||||
void DeSerialize(std::ifstream &caseStream);
|
||||
|
||||
/**
|
||||
* \brief Serializes the supplied event list and writes it to fd
|
||||
*
|
||||
* \param prefix If not NULL, this prefix will be prepended to
|
||||
* every event in the file.
|
||||
*/
|
||||
void SerializeEvList(const DevdCtl::EventList events, int fd,
|
||||
const char* prefix=NULL) const;
|
||||
|
||||
/**
|
||||
* \brief Unconditionally close a CaseFile.
|
||||
*/
|
||||
virtual void Close();
|
||||
|
||||
/**
|
||||
* \brief Callout callback invoked when the remove timer grace
|
||||
* period expires.
|
||||
*
|
||||
* If no remove events are received prior to the grace period
|
||||
* firing, then any tentative events are promoted and counted
|
||||
* against the health of the vdev.
|
||||
*/
|
||||
void OnGracePeriodEnded();
|
||||
|
||||
/**
|
||||
* \brief Attempt to activate a spare on this case's pool.
|
||||
*
|
||||
* Call this whenever a pool becomes degraded. It will look for any
|
||||
* spare devices and activate one to replace the casefile's vdev. It
|
||||
* will _not_ close the casefile; that should only happen when the
|
||||
* missing drive is replaced or the user promotes the spare.
|
||||
*
|
||||
* \return True if a spare was activated
|
||||
*/
|
||||
bool ActivateSpare();
|
||||
|
||||
/**
|
||||
* \brief replace a pool's vdev with another
|
||||
*
|
||||
* \param vdev_type The type of the new vdev. Usually either
|
||||
* VDEV_TYPE_DISK or VDEV_TYPE_FILE
|
||||
* \param path The file system path to the new vdev
|
||||
* \param isspare Whether the new vdev is a spare
|
||||
*
|
||||
* \return true iff the replacement was successful
|
||||
*/
|
||||
bool Replace(const char* vdev_type, const char* path, bool isspare);
|
||||
|
||||
/**
|
||||
* \brief Which vdev, if any, is replacing ours.
|
||||
*
|
||||
* \param zhp Pool handle state from the caller context
|
||||
*
|
||||
* \return the vdev that is currently replacing ours,
|
||||
* or NonexistentVdev if there isn't one.
|
||||
*/
|
||||
Vdev BeingReplacedBy(zpool_handle_t *zhp);
|
||||
|
||||
/**
|
||||
* \brief All CaseFiles being tracked by ZFSD.
|
||||
*/
|
||||
static CaseFileList s_activeCases;
|
||||
|
||||
/**
|
||||
* \brief The file system path to serialized CaseFile data.
|
||||
*/
|
||||
static const string s_caseFilePath;
|
||||
|
||||
/**
|
||||
* \brief The time ZFSD waits before promoting a tentative event
|
||||
* into a permanent event.
|
||||
*/
|
||||
static const timeval s_removeGracePeriod;
|
||||
|
||||
/**
|
||||
* \brief A list of soft error events counted against the health of
|
||||
* a vdev.
|
||||
*/
|
||||
DevdCtl::EventList m_events;
|
||||
|
||||
/**
|
||||
* \brief A list of soft error events waiting for a grace period
|
||||
* expiration before being counted against the health of
|
||||
* a vdev.
|
||||
*/
|
||||
DevdCtl::EventList m_tentativeEvents;
|
||||
|
||||
DevdCtl::Guid m_poolGUID;
|
||||
DevdCtl::Guid m_vdevGUID;
|
||||
vdev_state m_vdevState;
|
||||
string m_poolGUIDString;
|
||||
string m_vdevGUIDString;
|
||||
string m_vdevPhysPath;
|
||||
|
||||
/**
|
||||
* \brief Callout activated when a grace period
|
||||
*/
|
||||
Callout m_tentativeTimer;
|
||||
|
||||
private:
|
||||
nvlist_t *CaseVdev(zpool_handle_t *zhp) const;
|
||||
};
|
||||
|
||||
inline DevdCtl::Guid
|
||||
CaseFile::PoolGUID() const
|
||||
{
|
||||
return (m_poolGUID);
|
||||
}
|
||||
|
||||
inline DevdCtl::Guid
|
||||
CaseFile::VdevGUID() const
|
||||
{
|
||||
return (m_vdevGUID);
|
||||
}
|
||||
|
||||
inline vdev_state
|
||||
CaseFile::VdevState() const
|
||||
{
|
||||
return (m_vdevState);
|
||||
}
|
||||
|
||||
inline const string &
|
||||
CaseFile::PoolGUIDString() const
|
||||
{
|
||||
return (m_poolGUIDString);
|
||||
}
|
||||
|
||||
inline const string &
|
||||
CaseFile::VdevGUIDString() const
|
||||
{
|
||||
return (m_vdevGUIDString);
|
||||
}
|
||||
|
||||
inline const string &
|
||||
CaseFile::PhysicalPath() const
|
||||
{
|
||||
return (m_vdevPhysPath);
|
||||
}
|
||||
|
||||
#endif /* _CASE_FILE_H_ */
|
45
cddl/usr.sbin/zfsd/tests/Makefile
Normal file
45
cddl/usr.sbin/zfsd/tests/Makefile
Normal file
@ -0,0 +1,45 @@
|
||||
# $FreeBSD$
|
||||
|
||||
SRCDIR=${.CURDIR}/../../../..
|
||||
.include "${.CURDIR}/../Makefile.common"
|
||||
.PATH: ${.CURDIR}/..
|
||||
|
||||
TESTSDIR?= ${TESTSBASE}/cddl/sbin/zfsd
|
||||
|
||||
PLAIN_TESTS_CXX= zfsd_unittest
|
||||
SRCS.zfsd_unittest:= ${SRCS:Nzfsd_main.cc}
|
||||
SRCS.zfsd_unittest+= libmocks.c zfsd_unittest.cc
|
||||
SRCS=
|
||||
|
||||
# Use #include <zfsd/xxx.h> in test programs.
|
||||
INCFLAGS+= -I${.CURDIR}/../..
|
||||
|
||||
.if defined(DESTDIR)
|
||||
INCFLAGS+= -I${DESTDIR}/usr/include
|
||||
LIBRARY_PATH= ${DESTDIR}/lib:${DESTDIR}/usr/lib
|
||||
LDFLAGS.zfsd_unittest+= -L${DESTDIR}/lib -L${DESTDIR}/usr/lib
|
||||
.elif defined(WORLDTMP)
|
||||
INCFLAGS+= -I${WORLDTMP}/usr/include
|
||||
LIBRARY_PATH= ${WORLDTMP}/lib:${WORLDTMP}/usr/lib
|
||||
LDFLAGS.zfsd_unittest+= -L${WORLDTMP}/lib -L${WORLDTMP}/usr/lib
|
||||
.else
|
||||
LIBRARY_PATH=
|
||||
.endif
|
||||
|
||||
# Googletest options
|
||||
LOCALBASE?= /usr/local
|
||||
INCFLAGS+= -I${LOCALBASE}/include -D_THREAD_SAFE -pthread
|
||||
LDFLAGS.zfsd_unittest+= -L${LOCALBASE}/lib -D_THREAD_SAFE -pthread
|
||||
LDADD.zfsd_unittest+= ${LOCALBASE}/lib/libgtest.a
|
||||
|
||||
# GoogleMock options
|
||||
LDADD.zfsd_unittest+= ${LOCALBASE}/lib/libgmock.a ${LOCALBASE}/lib/libgmock_main.a
|
||||
|
||||
# Googlemock fails if we don't have this line
|
||||
# https://groups.google.com/forum/#!msg/googletestframework/h8ixEPCFm0o/amwfu4xGJb0J
|
||||
CFLAGS.zfsd_unittest+= -DGTEST_HAS_PTHREAD
|
||||
|
||||
# Install the tests
|
||||
TESTSBASE?= /usr/tests
|
||||
|
||||
.include <bsd.test.mk>
|
58
cddl/usr.sbin/zfsd/tests/libmocks.c
Normal file
58
cddl/usr.sbin/zfsd/tests/libmocks.c
Normal file
@ -0,0 +1,58 @@
|
||||
/*-
|
||||
* Copyright (c) 2012 Spectra Logic Corporation
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions, and the following disclaimer,
|
||||
* without modification.
|
||||
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
|
||||
* substantially similar to the "NO WARRANTY" disclaimer below
|
||||
* ("Disclaimer") and any redistribution must be conditioned upon
|
||||
* including a substantially similar Disclaimer requirement for further
|
||||
* binary redistribution.
|
||||
*
|
||||
* NO WARRANTY
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGES.
|
||||
*
|
||||
* Authors: Alan Somers (Spectra Logic Corporation)
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include "libmocks.h"
|
||||
|
||||
/*
|
||||
* This file mocks shared library functions that are used by zfsd. Every
|
||||
* function present will be used for all tests in all test suites instead of the
|
||||
* normal function.
|
||||
*/
|
||||
|
||||
int syslog_last_priority;
|
||||
char syslog_last_message[4096];
|
||||
void syslog(int priority, const char* message, ...) {
|
||||
va_list ap;
|
||||
|
||||
syslog_last_priority = priority;
|
||||
va_start(ap, message);
|
||||
vsnprintf(syslog_last_message, 4096, message, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
int zpool_iter(libzfs_handle_t* handle, zpool_iter_f iter, void* arg) {
|
||||
return (0);
|
||||
}
|
58
cddl/usr.sbin/zfsd/tests/libmocks.h
Normal file
58
cddl/usr.sbin/zfsd/tests/libmocks.h
Normal file
@ -0,0 +1,58 @@
|
||||
/*-
|
||||
* Copyright (c) 2012 Spectra Logic Corporation
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions, and the following disclaimer,
|
||||
* without modification.
|
||||
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
|
||||
* substantially similar to the "NO WARRANTY" disclaimer below
|
||||
* ("Disclaimer") and any redistribution must be conditioned upon
|
||||
* including a substantially similar Disclaimer requirement for further
|
||||
* binary redistribution.
|
||||
*
|
||||
* NO WARRANTY
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGES.
|
||||
*
|
||||
* Authors: Alan Somers (Spectra Logic Corporation)
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef _LIBMOCKS_H_
|
||||
#define _LIBMOCKS_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct libzfs_handle;
|
||||
typedef struct libzfs_handle libzfs_handle_t;
|
||||
struct zpool_handle;
|
||||
typedef struct zpool_handle zpool_handle_t;
|
||||
typedef int (*zpool_iter_f)(zpool_handle_t *, void *);
|
||||
|
||||
void syslog(int priority, const char* message, ...);
|
||||
int zpool_iter(libzfs_handle_t*, zpool_iter_f, void*);
|
||||
|
||||
extern int syslog_last_priority;
|
||||
extern char syslog_last_message[4096];
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
771
cddl/usr.sbin/zfsd/tests/zfsd_unittest.cc
Normal file
771
cddl/usr.sbin/zfsd/tests/zfsd_unittest.cc
Normal file
@ -0,0 +1,771 @@
|
||||
/*-
|
||||
* Copyright (c) 2012, 2013, 2014 Spectra Logic Corporation
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions, and the following disclaimer,
|
||||
* without modification.
|
||||
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
|
||||
* substantially similar to the "NO WARRANTY" disclaimer below
|
||||
* ("Disclaimer") and any redistribution must be conditioned upon
|
||||
* including a substantially similar Disclaimer requirement for further
|
||||
* binary redistribution.
|
||||
*
|
||||
* NO WARRANTY
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGES.
|
||||
*
|
||||
* Authors: Alan Somers (Spectra Logic Corporation)
|
||||
*/
|
||||
#include <sys/cdefs.h>
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <syslog.h>
|
||||
|
||||
#include <libnvpair.h>
|
||||
#include <libzfs.h>
|
||||
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <devdctl/guid.h>
|
||||
#include <devdctl/event.h>
|
||||
#include <devdctl/event_factory.h>
|
||||
#include <devdctl/exception.h>
|
||||
#include <devdctl/consumer.h>
|
||||
|
||||
#include <zfsd/callout.h>
|
||||
#include <zfsd/vdev_iterator.h>
|
||||
#include <zfsd/zfsd_event.h>
|
||||
#include <zfsd/case_file.h>
|
||||
#include <zfsd/vdev.h>
|
||||
#include <zfsd/zfsd.h>
|
||||
#include <zfsd/zfsd_exception.h>
|
||||
#include <zfsd/zpool_list.h>
|
||||
|
||||
#include "libmocks.h"
|
||||
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
/*================================== Macros ==================================*/
|
||||
#define NUM_ELEMENTS(x) (sizeof(x) / sizeof(*x))
|
||||
|
||||
/*============================ Namespace Control =============================*/
|
||||
using std::string;
|
||||
using std::stringstream;
|
||||
|
||||
using DevdCtl::Event;
|
||||
using DevdCtl::EventBuffer;
|
||||
using DevdCtl::EventFactory;
|
||||
using DevdCtl::EventList;
|
||||
using DevdCtl::Guid;
|
||||
using DevdCtl::NVPairMap;
|
||||
|
||||
/* redefine zpool_handle here because libzfs_impl.h is not includable */
|
||||
struct zpool_handle
|
||||
{
|
||||
libzfs_handle_t *zpool_hdl;
|
||||
zpool_handle_t *zpool_next;
|
||||
char zpool_name[ZPOOL_MAXNAMELEN];
|
||||
int zpool_state;
|
||||
size_t zpool_config_size;
|
||||
nvlist_t *zpool_config;
|
||||
nvlist_t *zpool_old_config;
|
||||
nvlist_t *zpool_props;
|
||||
diskaddr_t zpool_start_block;
|
||||
};
|
||||
|
||||
class MockZfsEvent : public ZfsEvent
|
||||
{
|
||||
public:
|
||||
MockZfsEvent(Event::Type, NVPairMap&, const string&);
|
||||
virtual ~MockZfsEvent() {}
|
||||
|
||||
static BuildMethod MockZfsEventBuilder;
|
||||
|
||||
MOCK_CONST_METHOD0(ProcessPoolEvent, void());
|
||||
|
||||
static EventFactory::Record s_buildRecords[];
|
||||
};
|
||||
|
||||
EventFactory::Record MockZfsEvent::s_buildRecords[] =
|
||||
{
|
||||
{ Event::NOTIFY, "ZFS", &MockZfsEvent::MockZfsEventBuilder }
|
||||
};
|
||||
|
||||
MockZfsEvent::MockZfsEvent(Event::Type type, NVPairMap& map,
|
||||
const string& str)
|
||||
: ZfsEvent(type, map, str)
|
||||
{
|
||||
}
|
||||
|
||||
Event *
|
||||
MockZfsEvent::MockZfsEventBuilder(Event::Type type,
|
||||
NVPairMap &nvpairs,
|
||||
const string &eventString)
|
||||
{
|
||||
return (new MockZfsEvent(type, nvpairs, eventString));
|
||||
}
|
||||
|
||||
/*
|
||||
* A dummy Vdev class used for testing other classes
|
||||
*/
|
||||
class MockVdev : public Vdev
|
||||
{
|
||||
public:
|
||||
MockVdev(nvlist_t *vdevConfig);
|
||||
virtual ~MockVdev() {}
|
||||
|
||||
MOCK_CONST_METHOD0(GUID, Guid());
|
||||
MOCK_CONST_METHOD0(PoolGUID, Guid());
|
||||
MOCK_CONST_METHOD0(State, vdev_state());
|
||||
MOCK_CONST_METHOD0(PhysicalPath, string());
|
||||
};
|
||||
|
||||
MockVdev::MockVdev(nvlist_t *vdevConfig)
|
||||
: Vdev(vdevConfig)
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* A CaseFile class with side effects removed, for testing
|
||||
*/
|
||||
class TestableCaseFile : public CaseFile
|
||||
{
|
||||
public:
|
||||
static TestableCaseFile &Create(Vdev &vdev);
|
||||
TestableCaseFile(Vdev &vdev);
|
||||
virtual ~TestableCaseFile() {}
|
||||
|
||||
MOCK_METHOD0(Close, void());
|
||||
MOCK_METHOD1(RegisterCallout, void(const Event &event));
|
||||
MOCK_METHOD0(RefreshVdevState, bool());
|
||||
MOCK_METHOD1(ReEvaluate, bool(const ZfsEvent &event));
|
||||
|
||||
bool RealReEvaluate(const ZfsEvent &event)
|
||||
{
|
||||
return (CaseFile::ReEvaluate(event));
|
||||
}
|
||||
|
||||
/*
|
||||
* This splices the event lists, a procedure that would normally be done
|
||||
* by OnGracePeriodEnded, but we don't necessarily call that in the
|
||||
* unit tests
|
||||
*/
|
||||
void SpliceEvents();
|
||||
|
||||
/*
|
||||
* Used by some of our expectations. CaseFile does not publicize this
|
||||
*/
|
||||
static int getActiveCases()
|
||||
{
|
||||
return (s_activeCases.size());
|
||||
}
|
||||
};
|
||||
|
||||
TestableCaseFile::TestableCaseFile(Vdev &vdev)
|
||||
: CaseFile(vdev)
|
||||
{
|
||||
}
|
||||
|
||||
TestableCaseFile &
|
||||
TestableCaseFile::Create(Vdev &vdev)
|
||||
{
|
||||
TestableCaseFile *newCase;
|
||||
newCase = new TestableCaseFile(vdev);
|
||||
return (*newCase);
|
||||
}
|
||||
|
||||
void
|
||||
TestableCaseFile::SpliceEvents()
|
||||
{
|
||||
m_events.splice(m_events.begin(), m_tentativeEvents);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Test class ZfsdException
|
||||
*/
|
||||
class ZfsdExceptionTest : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
virtual void SetUp()
|
||||
{
|
||||
ASSERT_EQ(0, nvlist_alloc(&poolConfig, NV_UNIQUE_NAME, 0));
|
||||
ASSERT_EQ(0, nvlist_add_string(poolConfig,
|
||||
ZPOOL_CONFIG_POOL_NAME, "unit_test_pool"));
|
||||
ASSERT_EQ(0, nvlist_add_uint64(poolConfig,
|
||||
ZPOOL_CONFIG_POOL_GUID, 0x1234));
|
||||
|
||||
ASSERT_EQ(0, nvlist_alloc(&vdevConfig, NV_UNIQUE_NAME, 0));
|
||||
ASSERT_EQ(0, nvlist_add_uint64(vdevConfig,
|
||||
ZPOOL_CONFIG_GUID, 0x5678));
|
||||
bzero(&poolHandle, sizeof(poolHandle));
|
||||
poolHandle.zpool_config = poolConfig;
|
||||
}
|
||||
|
||||
virtual void TearDown()
|
||||
{
|
||||
nvlist_free(poolConfig);
|
||||
nvlist_free(vdevConfig);
|
||||
}
|
||||
|
||||
nvlist_t *poolConfig;
|
||||
nvlist_t *vdevConfig;
|
||||
zpool_handle_t poolHandle;
|
||||
};
|
||||
|
||||
TEST_F(ZfsdExceptionTest, StringConstructorNull)
|
||||
{
|
||||
ZfsdException ze("");
|
||||
EXPECT_STREQ("", ze.GetString().c_str());
|
||||
}
|
||||
|
||||
TEST_F(ZfsdExceptionTest, StringConstructorFormatted)
|
||||
{
|
||||
ZfsdException ze(" %d %s", 55, "hello world");
|
||||
EXPECT_STREQ(" 55 hello world", ze.GetString().c_str());
|
||||
}
|
||||
|
||||
TEST_F(ZfsdExceptionTest, LogSimple)
|
||||
{
|
||||
ZfsdException ze("unit test w/o vdev or pool");
|
||||
ze.Log();
|
||||
EXPECT_EQ(LOG_ERR, syslog_last_priority);
|
||||
EXPECT_STREQ("unit test w/o vdev or pool\n", syslog_last_message);
|
||||
}
|
||||
|
||||
TEST_F(ZfsdExceptionTest, Pool)
|
||||
{
|
||||
const char msg[] = "Exception with pool name";
|
||||
char expected[4096];
|
||||
sprintf(expected, "Pool unit_test_pool: %s\n", msg);
|
||||
ZfsdException ze(poolConfig, msg);
|
||||
ze.Log();
|
||||
EXPECT_STREQ(expected, syslog_last_message);
|
||||
}
|
||||
|
||||
TEST_F(ZfsdExceptionTest, PoolHandle)
|
||||
{
|
||||
const char msg[] = "Exception with pool handle";
|
||||
char expected[4096];
|
||||
sprintf(expected, "Pool unit_test_pool: %s\n", msg);
|
||||
ZfsdException ze(&poolHandle, msg);
|
||||
ze.Log();
|
||||
EXPECT_STREQ(expected, syslog_last_message);
|
||||
}
|
||||
|
||||
/*
|
||||
* Test class Vdev
|
||||
*/
|
||||
class VdevTest : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
virtual void SetUp()
|
||||
{
|
||||
ASSERT_EQ(0, nvlist_alloc(&m_poolConfig, NV_UNIQUE_NAME, 0));
|
||||
ASSERT_EQ(0, nvlist_add_uint64(m_poolConfig,
|
||||
ZPOOL_CONFIG_POOL_GUID,
|
||||
0x1234));
|
||||
|
||||
ASSERT_EQ(0, nvlist_alloc(&m_vdevConfig, NV_UNIQUE_NAME, 0));
|
||||
ASSERT_EQ(0, nvlist_add_uint64(m_vdevConfig, ZPOOL_CONFIG_GUID,
|
||||
0x5678));
|
||||
}
|
||||
|
||||
virtual void TearDown()
|
||||
{
|
||||
nvlist_free(m_poolConfig);
|
||||
nvlist_free(m_vdevConfig);
|
||||
}
|
||||
|
||||
nvlist_t *m_poolConfig;
|
||||
nvlist_t *m_vdevConfig;
|
||||
};
|
||||
|
||||
|
||||
TEST_F(VdevTest, StateFromConfig)
|
||||
{
|
||||
vdev_stat_t vs;
|
||||
|
||||
vs.vs_state = VDEV_STATE_OFFLINE;
|
||||
|
||||
ASSERT_EQ(0, nvlist_add_uint64_array(m_vdevConfig,
|
||||
ZPOOL_CONFIG_VDEV_STATS,
|
||||
(uint64_t*)&vs,
|
||||
sizeof(vs) / sizeof(uint64_t)));
|
||||
|
||||
Vdev vdev(m_poolConfig, m_vdevConfig);
|
||||
|
||||
EXPECT_EQ(VDEV_STATE_OFFLINE, vdev.State());
|
||||
}
|
||||
|
||||
TEST_F(VdevTest, StateFaulted)
|
||||
{
|
||||
ASSERT_EQ(0, nvlist_add_uint64(m_vdevConfig, ZPOOL_CONFIG_FAULTED, 1));
|
||||
|
||||
Vdev vdev(m_poolConfig, m_vdevConfig);
|
||||
|
||||
EXPECT_EQ(VDEV_STATE_FAULTED, vdev.State());
|
||||
}
|
||||
|
||||
/*
|
||||
* Test that we can construct a Vdev from the label information that is stored
|
||||
* on an available spare drive
|
||||
*/
|
||||
TEST_F(VdevTest, ConstructAvailSpare)
|
||||
{
|
||||
nvlist_t *labelConfig;
|
||||
|
||||
ASSERT_EQ(0, nvlist_alloc(&labelConfig, NV_UNIQUE_NAME, 0));
|
||||
ASSERT_EQ(0, nvlist_add_uint64(labelConfig, ZPOOL_CONFIG_GUID,
|
||||
1948339428197961030));
|
||||
ASSERT_EQ(0, nvlist_add_uint64(labelConfig, ZPOOL_CONFIG_POOL_STATE,
|
||||
POOL_STATE_SPARE));
|
||||
|
||||
EXPECT_NO_THROW(Vdev vdev(labelConfig));
|
||||
|
||||
nvlist_free(labelConfig);
|
||||
}
|
||||
|
||||
/* Available spares will always show the HEALTHY state */
|
||||
TEST_F(VdevTest, AvailSpareState) {
|
||||
nvlist_t *labelConfig;
|
||||
|
||||
ASSERT_EQ(0, nvlist_alloc(&labelConfig, NV_UNIQUE_NAME, 0));
|
||||
ASSERT_EQ(0, nvlist_add_uint64(labelConfig, ZPOOL_CONFIG_GUID,
|
||||
1948339428197961030));
|
||||
ASSERT_EQ(0, nvlist_add_uint64(labelConfig, ZPOOL_CONFIG_POOL_STATE,
|
||||
POOL_STATE_SPARE));
|
||||
|
||||
Vdev vdev(labelConfig);
|
||||
EXPECT_EQ(VDEV_STATE_HEALTHY, vdev.State());
|
||||
|
||||
nvlist_free(labelConfig);
|
||||
}
|
||||
|
||||
/* Test the Vdev::IsSpare method */
|
||||
TEST_F(VdevTest, IsSpare) {
|
||||
Vdev notSpare(m_poolConfig, m_vdevConfig);
|
||||
EXPECT_EQ(false, notSpare.IsSpare());
|
||||
|
||||
ASSERT_EQ(0, nvlist_add_uint64(m_vdevConfig, ZPOOL_CONFIG_IS_SPARE, 1));
|
||||
Vdev isSpare(m_poolConfig, m_vdevConfig);
|
||||
EXPECT_EQ(true, isSpare.IsSpare());
|
||||
}
|
||||
|
||||
/*
|
||||
* Test class ZFSEvent
|
||||
*/
|
||||
class ZfsEventTest : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
virtual void SetUp()
|
||||
{
|
||||
m_eventFactory = new EventFactory();
|
||||
m_eventFactory->UpdateRegistry(MockZfsEvent::s_buildRecords,
|
||||
NUM_ELEMENTS(MockZfsEvent::s_buildRecords));
|
||||
|
||||
m_event = NULL;
|
||||
}
|
||||
|
||||
virtual void TearDown()
|
||||
{
|
||||
delete m_eventFactory;
|
||||
delete m_event;
|
||||
}
|
||||
|
||||
EventFactory *m_eventFactory;
|
||||
Event *m_event;
|
||||
};
|
||||
|
||||
TEST_F(ZfsEventTest, ProcessPoolEventGetsCalled)
|
||||
{
|
||||
string evString("!system=ZFS "
|
||||
"subsystem=ZFS "
|
||||
"type=misc.fs.zfs.vdev_remove "
|
||||
"pool_name=foo "
|
||||
"pool_guid=9756779504028057996 "
|
||||
"vdev_guid=1631193447431603339 "
|
||||
"vdev_path=/dev/da1 "
|
||||
"timestamp=1348871594");
|
||||
m_event = Event::CreateEvent(*m_eventFactory, evString);
|
||||
MockZfsEvent *mock_event = static_cast<MockZfsEvent*>(m_event);
|
||||
|
||||
EXPECT_CALL(*mock_event, ProcessPoolEvent()).Times(1);
|
||||
mock_event->Process();
|
||||
}
|
||||
|
||||
/*
|
||||
* Test class CaseFile
|
||||
*/
|
||||
|
||||
class CaseFileTest : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
virtual void SetUp()
|
||||
{
|
||||
m_eventFactory = new EventFactory();
|
||||
m_eventFactory->UpdateRegistry(MockZfsEvent::s_buildRecords,
|
||||
NUM_ELEMENTS(MockZfsEvent::s_buildRecords));
|
||||
|
||||
m_event = NULL;
|
||||
|
||||
nvlist_alloc(&m_vdevConfig, NV_UNIQUE_NAME, 0);
|
||||
ASSERT_EQ(0, nvlist_add_uint64(m_vdevConfig,
|
||||
ZPOOL_CONFIG_GUID, 0xbeef));
|
||||
m_vdev = new MockVdev(m_vdevConfig);
|
||||
ON_CALL(*m_vdev, GUID())
|
||||
.WillByDefault(::testing::Return(Guid(123)));
|
||||
ON_CALL(*m_vdev, PoolGUID())
|
||||
.WillByDefault(::testing::Return(Guid(456)));
|
||||
ON_CALL(*m_vdev, State())
|
||||
.WillByDefault(::testing::Return(VDEV_STATE_HEALTHY));
|
||||
m_caseFile = &TestableCaseFile::Create(*m_vdev);
|
||||
ON_CALL(*m_caseFile, ReEvaluate(::testing::_))
|
||||
.WillByDefault(::testing::Invoke(m_caseFile, &TestableCaseFile::RealReEvaluate));
|
||||
return;
|
||||
}
|
||||
|
||||
virtual void TearDown()
|
||||
{
|
||||
delete m_caseFile;
|
||||
nvlist_free(m_vdevConfig);
|
||||
delete m_vdev;
|
||||
delete m_event;
|
||||
delete m_eventFactory;
|
||||
}
|
||||
|
||||
nvlist_t *m_vdevConfig;
|
||||
MockVdev *m_vdev;
|
||||
TestableCaseFile *m_caseFile;
|
||||
Event *m_event;
|
||||
EventFactory *m_eventFactory;
|
||||
};
|
||||
|
||||
/*
|
||||
* A Vdev with no events should not be degraded or faulted
|
||||
*/
|
||||
TEST_F(CaseFileTest, HealthyVdev)
|
||||
{
|
||||
EXPECT_FALSE(m_caseFile->ShouldDegrade());
|
||||
EXPECT_FALSE(m_caseFile->ShouldFault());
|
||||
}
|
||||
|
||||
/*
|
||||
* A Vdev with only one event should not be degraded or faulted
|
||||
* For performance reasons, RefreshVdevState should not be called.
|
||||
*/
|
||||
TEST_F(CaseFileTest, HealthyishVdev)
|
||||
{
|
||||
string evString("!system=ZFS "
|
||||
"class=ereport.fs.zfs.io "
|
||||
"ena=12091638756982918145 "
|
||||
"parent_guid=13237004955564865395 "
|
||||
"parent_type=raidz "
|
||||
"pool=testpool.4415 "
|
||||
"pool_context=0 "
|
||||
"pool_failmode=wait "
|
||||
"pool_guid=456 "
|
||||
"subsystem=ZFS "
|
||||
"timestamp=1348867914 "
|
||||
"type=ereport.fs.zfs.io "
|
||||
"vdev_guid=123 "
|
||||
"vdev_path=/dev/da400 "
|
||||
"vdev_type=disk "
|
||||
"zio_blkid=622 "
|
||||
"zio_err=1 "
|
||||
"zio_level=-2 "
|
||||
"zio_object=0 "
|
||||
"zio_objset=37 "
|
||||
"zio_offset=25598976 "
|
||||
"zio_size=1024");
|
||||
m_event = Event::CreateEvent(*m_eventFactory, evString);
|
||||
ZfsEvent *zfs_event = static_cast<ZfsEvent*>(m_event);
|
||||
|
||||
EXPECT_CALL(*m_caseFile, RefreshVdevState())
|
||||
.Times(::testing::Exactly(0));
|
||||
EXPECT_TRUE(m_caseFile->ReEvaluate(*zfs_event));
|
||||
EXPECT_FALSE(m_caseFile->ShouldDegrade());
|
||||
EXPECT_FALSE(m_caseFile->ShouldFault());
|
||||
}
|
||||
|
||||
/* The case file should be closed when its pool is destroyed */
|
||||
TEST_F(CaseFileTest, PoolDestroy)
|
||||
{
|
||||
string evString("!system=ZFS "
|
||||
"pool_name=testpool.4415 "
|
||||
"pool_guid=456 "
|
||||
"subsystem=ZFS "
|
||||
"timestamp=1348867914 "
|
||||
"type=misc.fs.zfs.pool_destroy ");
|
||||
m_event = Event::CreateEvent(*m_eventFactory, evString);
|
||||
ZfsEvent *zfs_event = static_cast<ZfsEvent*>(m_event);
|
||||
EXPECT_CALL(*m_caseFile, Close());
|
||||
EXPECT_TRUE(m_caseFile->ReEvaluate(*zfs_event));
|
||||
}
|
||||
|
||||
/*
|
||||
* A Vdev with a very large number of IO errors should fault
|
||||
* For performance reasons, RefreshVdevState should be called at most once
|
||||
*/
|
||||
TEST_F(CaseFileTest, VeryManyIOErrors)
|
||||
{
|
||||
EXPECT_CALL(*m_caseFile, RefreshVdevState())
|
||||
.Times(::testing::AtMost(1))
|
||||
.WillRepeatedly(::testing::Return(true));
|
||||
|
||||
for(int i=0; i<100; i++) {
|
||||
stringstream evStringStream;
|
||||
evStringStream <<
|
||||
"!system=ZFS "
|
||||
"class=ereport.fs.zfs.io "
|
||||
"ena=12091638756982918145 "
|
||||
"parent_guid=13237004955564865395 "
|
||||
"parent_type=raidz "
|
||||
"pool=testpool.4415 "
|
||||
"pool_context=0 "
|
||||
"pool_failmode=wait "
|
||||
"pool_guid=456 "
|
||||
"subsystem=ZFS "
|
||||
"timestamp=";
|
||||
evStringStream << i << " ";
|
||||
evStringStream <<
|
||||
"type=ereport.fs.zfs.io "
|
||||
"vdev_guid=123 "
|
||||
"vdev_path=/dev/da400 "
|
||||
"vdev_type=disk "
|
||||
"zio_blkid=622 "
|
||||
"zio_err=1 "
|
||||
"zio_level=-2 "
|
||||
"zio_object=0 "
|
||||
"zio_objset=37 "
|
||||
"zio_offset=25598976 "
|
||||
"zio_size=1024";
|
||||
Event *event(Event::CreateEvent(*m_eventFactory,
|
||||
evStringStream.str()));
|
||||
ZfsEvent *zfs_event = static_cast<ZfsEvent*>(event);
|
||||
EXPECT_TRUE(m_caseFile->ReEvaluate(*zfs_event));
|
||||
delete event;
|
||||
}
|
||||
|
||||
m_caseFile->SpliceEvents();
|
||||
EXPECT_FALSE(m_caseFile->ShouldDegrade());
|
||||
EXPECT_TRUE(m_caseFile->ShouldFault());
|
||||
}
|
||||
|
||||
/*
|
||||
* A Vdev with a very large number of checksum errors should degrade
|
||||
* For performance reasons, RefreshVdevState should be called at most once
|
||||
*/
|
||||
TEST_F(CaseFileTest, VeryManyChecksumErrors)
|
||||
{
|
||||
EXPECT_CALL(*m_caseFile, RefreshVdevState())
|
||||
.Times(::testing::AtMost(1))
|
||||
.WillRepeatedly(::testing::Return(true));
|
||||
|
||||
for(int i=0; i<100; i++) {
|
||||
stringstream evStringStream;
|
||||
evStringStream <<
|
||||
"!system=ZFS "
|
||||
"bad_cleared_bits=03000000000000803f50b00000000000 "
|
||||
"bad_range_clears=0000000e "
|
||||
"bad_range_sets=00000000 "
|
||||
"bad_ranges=0000000000000010 "
|
||||
"bad_ranges_min_gap=8 "
|
||||
"bad_set_bits=00000000000000000000000000000000 "
|
||||
"class=ereport.fs.zfs.checksum "
|
||||
"ena=12272856582652437505 "
|
||||
"parent_guid=5838204195352909894 "
|
||||
"parent_type=raidz pool=testpool.7640 "
|
||||
"pool_context=0 "
|
||||
"pool_failmode=wait "
|
||||
"pool_guid=456 "
|
||||
"subsystem=ZFS timestamp=";
|
||||
evStringStream << i << " ";
|
||||
evStringStream <<
|
||||
"type=ereport.fs.zfs.checksum "
|
||||
"vdev_guid=123 "
|
||||
"vdev_path=/mnt/tmp/file1.7702 "
|
||||
"vdev_type=file "
|
||||
"zio_blkid=0 "
|
||||
"zio_err=0 "
|
||||
"zio_level=0 "
|
||||
"zio_object=3 "
|
||||
"zio_objset=0 "
|
||||
"zio_offset=16896 "
|
||||
"zio_size=512";
|
||||
Event *event(Event::CreateEvent(*m_eventFactory,
|
||||
evStringStream.str()));
|
||||
ZfsEvent *zfs_event = static_cast<ZfsEvent*>(event);
|
||||
EXPECT_TRUE(m_caseFile->ReEvaluate(*zfs_event));
|
||||
delete event;
|
||||
}
|
||||
|
||||
m_caseFile->SpliceEvents();
|
||||
EXPECT_TRUE(m_caseFile->ShouldDegrade());
|
||||
EXPECT_FALSE(m_caseFile->ShouldFault());
|
||||
}
|
||||
|
||||
/*
|
||||
* Test CaseFile::ReEvaluateByGuid
|
||||
*/
|
||||
class ReEvaluateByGuidTest : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
virtual void SetUp()
|
||||
{
|
||||
m_eventFactory = new EventFactory();
|
||||
m_eventFactory->UpdateRegistry(MockZfsEvent::s_buildRecords,
|
||||
NUM_ELEMENTS(MockZfsEvent::s_buildRecords));
|
||||
m_event = Event::CreateEvent(*m_eventFactory, s_evString);
|
||||
nvlist_alloc(&m_vdevConfig, NV_UNIQUE_NAME, 0);
|
||||
ASSERT_EQ(0, nvlist_add_uint64(m_vdevConfig,
|
||||
ZPOOL_CONFIG_GUID, 0xbeef));
|
||||
m_vdev456 = new ::testing::NiceMock<MockVdev>(m_vdevConfig);
|
||||
m_vdev789 = new ::testing::NiceMock<MockVdev>(m_vdevConfig);
|
||||
ON_CALL(*m_vdev456, GUID())
|
||||
.WillByDefault(::testing::Return(Guid(123)));
|
||||
ON_CALL(*m_vdev456, PoolGUID())
|
||||
.WillByDefault(::testing::Return(Guid(456)));
|
||||
ON_CALL(*m_vdev456, State())
|
||||
.WillByDefault(::testing::Return(VDEV_STATE_HEALTHY));
|
||||
ON_CALL(*m_vdev789, GUID())
|
||||
.WillByDefault(::testing::Return(Guid(123)));
|
||||
ON_CALL(*m_vdev789, PoolGUID())
|
||||
.WillByDefault(::testing::Return(Guid(789)));
|
||||
ON_CALL(*m_vdev789, State())
|
||||
.WillByDefault(::testing::Return(VDEV_STATE_HEALTHY));
|
||||
m_caseFile456 = NULL;
|
||||
m_caseFile789 = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
virtual void TearDown()
|
||||
{
|
||||
delete m_caseFile456;
|
||||
delete m_caseFile789;
|
||||
nvlist_free(m_vdevConfig);
|
||||
delete m_vdev456;
|
||||
delete m_vdev789;
|
||||
delete m_event;
|
||||
delete m_eventFactory;
|
||||
}
|
||||
|
||||
static string s_evString;
|
||||
nvlist_t *m_vdevConfig;
|
||||
::testing::NiceMock<MockVdev> *m_vdev456;
|
||||
::testing::NiceMock<MockVdev> *m_vdev789;
|
||||
TestableCaseFile *m_caseFile456;
|
||||
TestableCaseFile *m_caseFile789;
|
||||
Event *m_event;
|
||||
EventFactory *m_eventFactory;
|
||||
};
|
||||
|
||||
string ReEvaluateByGuidTest::s_evString(
|
||||
"!system=ZFS "
|
||||
"pool_guid=16271873792808333580 "
|
||||
"pool_name=foo "
|
||||
"subsystem=ZFS "
|
||||
"timestamp=1360620391 "
|
||||
"type=misc.fs.zfs.config_sync");
|
||||
|
||||
|
||||
/*
|
||||
* Test the ReEvaluateByGuid method on an empty list of casefiles.
|
||||
* We must create one event, even though it never gets used, because it will
|
||||
* be passed by reference to ReEvaluateByGuid
|
||||
*/
|
||||
TEST_F(ReEvaluateByGuidTest, ReEvaluateByGuid_empty)
|
||||
{
|
||||
ZfsEvent *zfs_event = static_cast<ZfsEvent*>(m_event);
|
||||
|
||||
EXPECT_EQ(0, TestableCaseFile::getActiveCases());
|
||||
CaseFile::ReEvaluateByGuid(Guid(456), *zfs_event);
|
||||
EXPECT_EQ(0, TestableCaseFile::getActiveCases());
|
||||
}
|
||||
|
||||
/*
|
||||
* Test the ReEvaluateByGuid method on a list of CaseFiles that contains only
|
||||
* one CaseFile, which doesn't match the criteria
|
||||
*/
|
||||
TEST_F(ReEvaluateByGuidTest, ReEvaluateByGuid_oneFalse)
|
||||
{
|
||||
m_caseFile456 = &TestableCaseFile::Create(*m_vdev456);
|
||||
ZfsEvent *zfs_event = static_cast<ZfsEvent*>(m_event);
|
||||
|
||||
EXPECT_EQ(1, TestableCaseFile::getActiveCases());
|
||||
EXPECT_CALL(*m_caseFile456, ReEvaluate(::testing::_))
|
||||
.Times(::testing::Exactly(0));
|
||||
CaseFile::ReEvaluateByGuid(Guid(789), *zfs_event);
|
||||
EXPECT_EQ(1, TestableCaseFile::getActiveCases());
|
||||
}
|
||||
|
||||
/*
|
||||
* Test the ReEvaluateByGuid method on a list of CaseFiles that contains only
|
||||
* one CaseFile, which does match the criteria
|
||||
*/
|
||||
TEST_F(ReEvaluateByGuidTest, ReEvaluateByGuid_oneTrue)
|
||||
{
|
||||
m_caseFile456 = &TestableCaseFile::Create(*m_vdev456);
|
||||
ZfsEvent *zfs_event = static_cast<ZfsEvent*>(m_event);
|
||||
|
||||
EXPECT_EQ(1, TestableCaseFile::getActiveCases());
|
||||
EXPECT_CALL(*m_caseFile456, ReEvaluate(::testing::_))
|
||||
.Times(::testing::Exactly(1))
|
||||
.WillRepeatedly(::testing::Return(false));
|
||||
CaseFile::ReEvaluateByGuid(Guid(456), *zfs_event);
|
||||
EXPECT_EQ(1, TestableCaseFile::getActiveCases());
|
||||
}
|
||||
|
||||
/*
|
||||
* Test the ReEvaluateByGuid method on a long list of CaseFiles that contains a
|
||||
* few cases which meet the criteria
|
||||
*/
|
||||
TEST_F(ReEvaluateByGuidTest, ReEvaluateByGuid_five)
|
||||
{
|
||||
TestableCaseFile *CaseFile1 = &TestableCaseFile::Create(*m_vdev456);
|
||||
TestableCaseFile *CaseFile2 = &TestableCaseFile::Create(*m_vdev789);
|
||||
TestableCaseFile *CaseFile3 = &TestableCaseFile::Create(*m_vdev456);
|
||||
TestableCaseFile *CaseFile4 = &TestableCaseFile::Create(*m_vdev789);
|
||||
TestableCaseFile *CaseFile5 = &TestableCaseFile::Create(*m_vdev789);
|
||||
ZfsEvent *zfs_event = static_cast<ZfsEvent*>(m_event);
|
||||
|
||||
EXPECT_EQ(5, TestableCaseFile::getActiveCases());
|
||||
EXPECT_CALL(*CaseFile1, ReEvaluate(::testing::_))
|
||||
.Times(::testing::Exactly(1))
|
||||
.WillRepeatedly(::testing::Return(false));
|
||||
EXPECT_CALL(*CaseFile3, ReEvaluate(::testing::_))
|
||||
.Times(::testing::Exactly(1))
|
||||
.WillRepeatedly(::testing::Return(false));
|
||||
EXPECT_CALL(*CaseFile2, ReEvaluate(::testing::_))
|
||||
.Times(::testing::Exactly(0));
|
||||
EXPECT_CALL(*CaseFile4, ReEvaluate(::testing::_))
|
||||
.Times(::testing::Exactly(0));
|
||||
EXPECT_CALL(*CaseFile5, ReEvaluate(::testing::_))
|
||||
.Times(::testing::Exactly(0));
|
||||
CaseFile::ReEvaluateByGuid(Guid(456), *zfs_event);
|
||||
EXPECT_EQ(5, TestableCaseFile::getActiveCases());
|
||||
delete CaseFile1;
|
||||
delete CaseFile2;
|
||||
delete CaseFile3;
|
||||
delete CaseFile4;
|
||||
delete CaseFile5;
|
||||
}
|
138
cddl/usr.sbin/zfsd/tests/zfsd_unittest.supp
Normal file
138
cddl/usr.sbin/zfsd/tests/zfsd_unittest.supp
Normal file
@ -0,0 +1,138 @@
|
||||
#-
|
||||
# Copyright (c) 2012 Spectra Logic Corporation
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions, and the following disclaimer,
|
||||
# without modification.
|
||||
# 2. Redistributions in binary form must reproduce at minimum a disclaimer
|
||||
# substantially similar to the "NO WARRANTY" disclaimer below
|
||||
# ("Disclaimer") and any redistribution must be conditioned upon
|
||||
# including a substantially similar Disclaimer requirement for further
|
||||
# binary redistribution.
|
||||
#
|
||||
# NO WARRANTY
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGES.
|
||||
#
|
||||
# Authors: Alan Somers (Spectra Logic Corporation)
|
||||
#
|
||||
# $FreeBSD$
|
||||
|
||||
|
||||
# This is a valgrind suppression file used for running zfsd_unittest with
|
||||
# valgrind. It suppress spurious errors generated by the googletest and
|
||||
# googlemock libraries.
|
||||
#
|
||||
# To use, do:
|
||||
# valgrind --suppressions=$PWD/zfsd_unittest.supp ./zfsd_unittest
|
||||
|
||||
{
|
||||
<insert_a_suppression_name_here>
|
||||
Memcheck:Free
|
||||
fun:free
|
||||
...
|
||||
fun:__cxa_finalize
|
||||
fun:exit
|
||||
fun:(below main)
|
||||
}
|
||||
|
||||
{
|
||||
<insert_a_suppression_name_here>
|
||||
Memcheck:Free
|
||||
fun:free
|
||||
...
|
||||
fun:_ZN7testing8internal27PrettyUnitTestResultPrinter*
|
||||
...
|
||||
...
|
||||
fun:main
|
||||
}
|
||||
|
||||
{
|
||||
<insert_a_suppression_name_here>
|
||||
Memcheck:Free
|
||||
fun:free
|
||||
fun:_ZN7testing*
|
||||
...
|
||||
fun:main
|
||||
}
|
||||
|
||||
{
|
||||
<insert_a_suppression_name_here>
|
||||
Memcheck:Free
|
||||
fun:free
|
||||
...
|
||||
fun:_Z41__static_initialization_and_destruction_0ii
|
||||
...
|
||||
}
|
||||
|
||||
{
|
||||
<insert_a_suppression_name_here>
|
||||
Memcheck:Free
|
||||
fun:free
|
||||
...
|
||||
fun:_ZN7testing8internal8MockSpec*
|
||||
...
|
||||
fun:_ZN7testing4Test3RunEv
|
||||
fun:_ZN7testing8internal12TestInfoImpl3RunEv
|
||||
fun:_ZN7testing8TestCase3RunEv
|
||||
fun:_ZN7testing8internal12UnitTestImpl11RunAllTestsEv
|
||||
}
|
||||
|
||||
{
|
||||
<insert_a_suppression_name_here>
|
||||
Memcheck:Free
|
||||
fun:free
|
||||
...
|
||||
fun:_ZN7testing8internal14FunctionMocker*
|
||||
...
|
||||
}
|
||||
|
||||
{
|
||||
<insert_a_suppression_name_here>
|
||||
Memcheck:Cond
|
||||
obj:/lib/libc.so.7
|
||||
obj:/lib/libc.so.7
|
||||
fun:snprintf
|
||||
fun:_ZN7testing45_GLOBAL__N_src_gmock_all.cc_00000000_917CAD5926PrintByteSegmentInObjectToEPKhmmPSo
|
||||
fun:_ZN7testing9internal220PrintBytesInObjectToEPKhmPSo
|
||||
fun:_ZN7testing9internal220TypeWithoutFormatterI8ZfsEventLb0EE10PrintValueERKS2_PSo
|
||||
fun:_ZN7testing9internal2lsIcSt11char_traitsIcE8ZfsEventEERSt13basic_ostreamIT_T0_ES9_RKT1_
|
||||
fun:_ZN16testing_internal26DefaultPrintNonContainerToI8ZfsEventEEvRKT_PSo
|
||||
fun:_ZN7testing8internal14DefaultPrintToI8ZfsEventEEvcNS0_13bool_constantILb0EEERKT_PSo
|
||||
fun:_ZN7testing8internal7PrintToI8ZfsEventEEvRKT_PSo
|
||||
fun:_ZN7testing8internal16UniversalPrinterIK8ZfsEventE5PrintERS3_PSo
|
||||
fun:_ZN7testing8internal16UniversalPrinterIRK8ZfsEventE5PrintES4_PSo
|
||||
}
|
||||
|
||||
{
|
||||
<insert_a_suppression_name_here>
|
||||
Memcheck:Cond
|
||||
...
|
||||
fun:snprintf
|
||||
...
|
||||
fun:_ZN7testing9internal220PrintBytesInObjectToEPKhmPSo
|
||||
...
|
||||
}
|
||||
{
|
||||
<insert_a_suppression_name_here>
|
||||
Memcheck:Value8
|
||||
...
|
||||
fun:snprintf
|
||||
...
|
||||
fun:_ZN7testing9internal220PrintBytesInObjectToEPKhmPSo
|
||||
...
|
||||
}
|
||||
|
357
cddl/usr.sbin/zfsd/vdev.cc
Normal file
357
cddl/usr.sbin/zfsd/vdev.cc
Normal file
@ -0,0 +1,357 @@
|
||||
/*-
|
||||
* Copyright (c) 2011, 2012, 2013, 2014 Spectra Logic Corporation
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions, and the following disclaimer,
|
||||
* without modification.
|
||||
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
|
||||
* substantially similar to the "NO WARRANTY" disclaimer below
|
||||
* ("Disclaimer") and any redistribution must be conditioned upon
|
||||
* including a substantially similar Disclaimer requirement for further
|
||||
* binary redistribution.
|
||||
*
|
||||
* NO WARRANTY
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGES.
|
||||
*
|
||||
* Authors: Justin T. Gibbs (Spectra Logic Corporation)
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file vdev.cc
|
||||
*
|
||||
* Implementation of the Vdev class.
|
||||
*/
|
||||
#include <syslog.h>
|
||||
#include <sys/cdefs.h>
|
||||
#include <sys/fs/zfs.h>
|
||||
|
||||
#include <libzfs.h>
|
||||
/*
|
||||
* Undefine flush, defined by cpufunc.h on sparc64, because it conflicts with
|
||||
* C++ flush methods
|
||||
*/
|
||||
#undef flush
|
||||
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
#include <devdctl/guid.h>
|
||||
#include <devdctl/event.h>
|
||||
#include <devdctl/event_factory.h>
|
||||
#include <devdctl/exception.h>
|
||||
#include <devdctl/consumer.h>
|
||||
|
||||
#include "vdev.h"
|
||||
#include "vdev_iterator.h"
|
||||
#include "zfsd.h"
|
||||
#include "zfsd_exception.h"
|
||||
#include "zpool_list.h"
|
||||
|
||||
__FBSDID("$FreeBSD$");
|
||||
/*============================ Namespace Control =============================*/
|
||||
using std::string;
|
||||
using std::stringstream;
|
||||
|
||||
//- Special objects -----------------------------------------------------------
|
||||
Vdev NonexistentVdev;
|
||||
|
||||
//- Vdev Inline Public Methods ------------------------------------------------
|
||||
/*=========================== Class Implementations ==========================*/
|
||||
/*----------------------------------- Vdev -----------------------------------*/
|
||||
|
||||
/* Special constructor for NonexistentVdev. */
|
||||
Vdev::Vdev()
|
||||
: m_poolConfig(NULL),
|
||||
m_config(NULL)
|
||||
{}
|
||||
|
||||
bool
|
||||
Vdev::VdevLookupPoolGuid()
|
||||
{
|
||||
uint64_t guid;
|
||||
if (nvlist_lookup_uint64(m_poolConfig, ZPOOL_CONFIG_POOL_GUID, &guid))
|
||||
return (false);
|
||||
m_poolGUID = guid;
|
||||
return (true);
|
||||
}
|
||||
|
||||
void
|
||||
Vdev::VdevLookupGuid()
|
||||
{
|
||||
uint64_t guid;
|
||||
if (nvlist_lookup_uint64(m_config, ZPOOL_CONFIG_GUID, &guid) != 0)
|
||||
throw ZfsdException("Unable to extract vdev GUID "
|
||||
"from vdev config data.");
|
||||
m_vdevGUID = guid;
|
||||
}
|
||||
|
||||
Vdev::Vdev(zpool_handle_t *pool, nvlist_t *config)
|
||||
: m_poolConfig(zpool_get_config(pool, NULL)),
|
||||
m_config(config)
|
||||
{
|
||||
if (!VdevLookupPoolGuid())
|
||||
throw ZfsdException("Can't extract pool GUID from handle.");
|
||||
VdevLookupGuid();
|
||||
}
|
||||
|
||||
Vdev::Vdev(nvlist_t *poolConfig, nvlist_t *config)
|
||||
: m_poolConfig(poolConfig),
|
||||
m_config(config)
|
||||
{
|
||||
if (!VdevLookupPoolGuid())
|
||||
throw ZfsdException("Can't extract pool GUID from config.");
|
||||
VdevLookupGuid();
|
||||
}
|
||||
|
||||
Vdev::Vdev(nvlist_t *labelConfig)
|
||||
: m_poolConfig(labelConfig),
|
||||
m_config(labelConfig)
|
||||
{
|
||||
/*
|
||||
* Spares do not have a Pool GUID. Tolerate its absence.
|
||||
* Code accessing this Vdev in a context where the Pool GUID is
|
||||
* required will find it invalid (as it is upon Vdev construction)
|
||||
* and act accordingly.
|
||||
*/
|
||||
(void) VdevLookupPoolGuid();
|
||||
VdevLookupGuid();
|
||||
|
||||
try {
|
||||
m_config = VdevIterator(labelConfig).Find(m_vdevGUID);
|
||||
} catch (const ZfsdException &exp) {
|
||||
/*
|
||||
* When reading a spare's label, it is normal not to find
|
||||
* a list of vdevs
|
||||
*/
|
||||
m_config = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
Vdev::IsSpare() const
|
||||
{
|
||||
uint64_t is_spare(0);
|
||||
|
||||
if (m_config == NULL)
|
||||
return (false);
|
||||
|
||||
(void)nvlist_lookup_uint64(m_config, ZPOOL_CONFIG_IS_SPARE, &is_spare);
|
||||
return (bool(is_spare));
|
||||
}
|
||||
|
||||
vdev_state
|
||||
Vdev::State() const
|
||||
{
|
||||
uint64_t *nvlist_array;
|
||||
vdev_stat_t *vs;
|
||||
uint_t vsc;
|
||||
|
||||
if (m_config == NULL) {
|
||||
/*
|
||||
* If we couldn't find the list of vdevs, that normally means
|
||||
* that this is an available hotspare. In that case, we will
|
||||
* presume it to be healthy. Even if this spare had formerly
|
||||
* been in use, been degraded, and been replaced, the act of
|
||||
* replacement wipes the degraded bit from the label. So we
|
||||
* have no choice but to presume that it is healthy.
|
||||
*/
|
||||
return (VDEV_STATE_HEALTHY);
|
||||
}
|
||||
|
||||
if (nvlist_lookup_uint64_array(m_config, ZPOOL_CONFIG_VDEV_STATS,
|
||||
&nvlist_array, &vsc) == 0) {
|
||||
vs = reinterpret_cast<vdev_stat_t *>(nvlist_array);
|
||||
return (static_cast<vdev_state>(vs->vs_state));
|
||||
}
|
||||
|
||||
/*
|
||||
* Stats are not available. This vdev was created from a label.
|
||||
* Synthesize a state based on available data.
|
||||
*/
|
||||
uint64_t faulted(0);
|
||||
uint64_t degraded(0);
|
||||
(void)nvlist_lookup_uint64(m_config, ZPOOL_CONFIG_FAULTED, &faulted);
|
||||
(void)nvlist_lookup_uint64(m_config, ZPOOL_CONFIG_DEGRADED, °raded);
|
||||
if (faulted)
|
||||
return (VDEV_STATE_FAULTED);
|
||||
if (degraded)
|
||||
return (VDEV_STATE_DEGRADED);
|
||||
return (VDEV_STATE_HEALTHY);
|
||||
}
|
||||
|
||||
std::list<Vdev>
|
||||
Vdev::Children()
|
||||
{
|
||||
nvlist_t **vdevChildren;
|
||||
int result;
|
||||
u_int numChildren;
|
||||
std::list<Vdev> children;
|
||||
|
||||
if (m_poolConfig == NULL || m_config == NULL)
|
||||
return (children);
|
||||
|
||||
result = nvlist_lookup_nvlist_array(m_config,
|
||||
ZPOOL_CONFIG_CHILDREN, &vdevChildren, &numChildren);
|
||||
if (result != 0)
|
||||
return (children);
|
||||
|
||||
for (u_int c = 0;c < numChildren; c++)
|
||||
children.push_back(Vdev(m_poolConfig, vdevChildren[c]));
|
||||
|
||||
return (children);
|
||||
}
|
||||
|
||||
Vdev
|
||||
Vdev::RootVdev()
|
||||
{
|
||||
nvlist_t *rootVdev;
|
||||
|
||||
if (m_poolConfig == NULL)
|
||||
return (NonexistentVdev);
|
||||
|
||||
if (nvlist_lookup_nvlist(m_poolConfig, ZPOOL_CONFIG_VDEV_TREE,
|
||||
&rootVdev) != 0)
|
||||
return (NonexistentVdev);
|
||||
return (Vdev(m_poolConfig, rootVdev));
|
||||
}
|
||||
|
||||
/*
|
||||
* Find our parent. This requires doing a traversal of the config; we can't
|
||||
* cache it as leaf vdevs may change their pool config location (spare,
|
||||
* replacing, mirror, etc).
|
||||
*/
|
||||
Vdev
|
||||
Vdev::Parent()
|
||||
{
|
||||
std::list<Vdev> to_examine;
|
||||
std::list<Vdev> children;
|
||||
std::list<Vdev>::iterator children_it;
|
||||
|
||||
to_examine.push_back(RootVdev());
|
||||
for (;;) {
|
||||
if (to_examine.empty())
|
||||
return (NonexistentVdev);
|
||||
Vdev vd = to_examine.front();
|
||||
if (vd.DoesNotExist())
|
||||
return (NonexistentVdev);
|
||||
to_examine.pop_front();
|
||||
children = vd.Children();
|
||||
children_it = children.begin();
|
||||
for (;children_it != children.end(); children_it++) {
|
||||
Vdev child = *children_it;
|
||||
|
||||
if (child.GUID() == GUID())
|
||||
return (vd);
|
||||
to_examine.push_front(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
Vdev::IsAvailableSpare() const
|
||||
{
|
||||
/* If we have a pool guid, we cannot be an available spare. */
|
||||
if (PoolGUID())
|
||||
return (false);
|
||||
|
||||
return (true);
|
||||
}
|
||||
|
||||
bool
|
||||
Vdev::IsSpare()
|
||||
{
|
||||
uint64_t spare;
|
||||
if (nvlist_lookup_uint64(m_config, ZPOOL_CONFIG_IS_SPARE, &spare) != 0)
|
||||
return (false);
|
||||
return (spare != 0);
|
||||
}
|
||||
|
||||
bool
|
||||
Vdev::IsActiveSpare() const
|
||||
{
|
||||
vdev_stat_t *vs;
|
||||
uint_t c;
|
||||
|
||||
if (m_poolConfig == NULL)
|
||||
return (false);
|
||||
|
||||
(void) nvlist_lookup_uint64_array(m_config, ZPOOL_CONFIG_VDEV_STATS,
|
||||
reinterpret_cast<uint64_t **>(&vs), &c);
|
||||
if (vs == NULL || vs->vs_aux != VDEV_AUX_SPARED)
|
||||
return (false);
|
||||
return (true);
|
||||
}
|
||||
|
||||
bool
|
||||
Vdev::IsResilvering() const
|
||||
{
|
||||
pool_scan_stat_t *ps = NULL;
|
||||
uint_t c;
|
||||
|
||||
if (State() != VDEV_STATE_HEALTHY)
|
||||
return (false);
|
||||
|
||||
(void) nvlist_lookup_uint64_array(m_config, ZPOOL_CONFIG_SCAN_STATS,
|
||||
reinterpret_cast<uint64_t **>(&ps), &c);
|
||||
if (ps == NULL || ps->pss_func != POOL_SCAN_RESILVER)
|
||||
return (false);
|
||||
return (true);
|
||||
}
|
||||
|
||||
string
|
||||
Vdev::GUIDString() const
|
||||
{
|
||||
stringstream vdevGUIDString;
|
||||
|
||||
vdevGUIDString << GUID();
|
||||
return (vdevGUIDString.str());
|
||||
}
|
||||
|
||||
string
|
||||
Vdev::Name(zpool_handle_t *zhp, bool verbose) const
|
||||
{
|
||||
return (zpool_vdev_name(g_zfsHandle, zhp, m_config,
|
||||
verbose ? B_TRUE : B_FALSE));
|
||||
}
|
||||
|
||||
string
|
||||
Vdev::Path() const
|
||||
{
|
||||
char *path(NULL);
|
||||
|
||||
if ((m_config != NULL)
|
||||
&& (nvlist_lookup_string(m_config, ZPOOL_CONFIG_PATH, &path) == 0))
|
||||
return (path);
|
||||
|
||||
return ("");
|
||||
}
|
||||
|
||||
string
|
||||
Vdev::PhysicalPath() const
|
||||
{
|
||||
char *path(NULL);
|
||||
|
||||
if ((m_config != NULL) && (nvlist_lookup_string(m_config,
|
||||
ZPOOL_CONFIG_PHYS_PATH, &path) == 0))
|
||||
return (path);
|
||||
|
||||
return ("");
|
||||
}
|
178
cddl/usr.sbin/zfsd/vdev.h
Normal file
178
cddl/usr.sbin/zfsd/vdev.h
Normal file
@ -0,0 +1,178 @@
|
||||
/*-
|
||||
* Copyright (c) 2011, 2012, 2013 Spectra Logic Corporation
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions, and the following disclaimer,
|
||||
* without modification.
|
||||
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
|
||||
* substantially similar to the "NO WARRANTY" disclaimer below
|
||||
* ("Disclaimer") and any redistribution must be conditioned upon
|
||||
* including a substantially similar Disclaimer requirement for further
|
||||
* binary redistribution.
|
||||
*
|
||||
* NO WARRANTY
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGES.
|
||||
*
|
||||
* Authors: Justin T. Gibbs (Spectra Logic Corporation)
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file vdev.h
|
||||
*
|
||||
* Definition of the Vdev class.
|
||||
*
|
||||
* Header requirements:
|
||||
*
|
||||
* #include <string>
|
||||
* #include <list>
|
||||
*
|
||||
* #include <devdctl/guid.h>
|
||||
*/
|
||||
#ifndef _VDEV_H_
|
||||
#define _VDEV_H_
|
||||
|
||||
/*=========================== Forward Declarations ===========================*/
|
||||
struct zpool_handle;
|
||||
typedef struct zpool_handle zpool_handle_t;
|
||||
|
||||
struct nvlist;
|
||||
typedef struct nvlist nvlist_t;
|
||||
|
||||
/*============================= Class Definitions ============================*/
|
||||
/*----------------------------------- Vdev -----------------------------------*/
|
||||
/**
|
||||
* \brief Wrapper class for a vdev's name/value configuration list
|
||||
* simplifying access to commonly used vdev attributes.
|
||||
*/
|
||||
class Vdev
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* \brief Instantiate a vdev object for a vdev that is a member
|
||||
* of an imported pool.
|
||||
*
|
||||
* \param pool The pool object containing the vdev with
|
||||
* configuration data provided in vdevConfig.
|
||||
* \param vdevConfig Vdev configuration data.
|
||||
*
|
||||
* This method should be used whenever dealing with vdev's
|
||||
* enumerated via the ZpoolList class. The in-core configuration
|
||||
* data for a vdev does not contain all of the items found in
|
||||
* the on-disk label. This requires the vdev class to augment
|
||||
* the data in vdevConfig with data found in the pool object.
|
||||
*/
|
||||
Vdev(zpool_handle_t *pool, nvlist_t *vdevConfig);
|
||||
|
||||
/**
|
||||
* \brief Instantiate a vdev object for a vdev that is a member
|
||||
* of a pool configuration.
|
||||
*
|
||||
* \param poolConfig The pool configuration containing the vdev
|
||||
* configuration data provided in vdevConfig.
|
||||
* \param vdevConfig Vdev configuration data.
|
||||
*
|
||||
* This method should be used whenever dealing with vdev's
|
||||
* enumerated via the ZpoolList class. The in-core configuration
|
||||
* data for a vdev does not contain all of the items found in
|
||||
* the on-disk label. This requires the vdev class to augment
|
||||
* the data in vdevConfig with data found in the pool object.
|
||||
*/
|
||||
Vdev(nvlist_t *poolConfig, nvlist_t *vdevConfig);
|
||||
|
||||
/**
|
||||
* \brief Instantiate a vdev object from a ZFS label stored on
|
||||
* the device.
|
||||
*
|
||||
* \param vdevConfig The name/value list retrieved by reading
|
||||
* the label information on a leaf vdev.
|
||||
*/
|
||||
Vdev(nvlist_t *vdevConfig);
|
||||
|
||||
/**
|
||||
* \brief No-op copy constructor for nonexistent vdevs.
|
||||
*/
|
||||
Vdev();
|
||||
bool DoesNotExist() const;
|
||||
|
||||
/**
|
||||
* \brief Return a list of the vdev's children.
|
||||
*/
|
||||
std::list<Vdev> Children();
|
||||
|
||||
virtual DevdCtl::Guid GUID() const;
|
||||
bool IsSpare() const;
|
||||
virtual DevdCtl::Guid PoolGUID() const;
|
||||
virtual vdev_state State() const;
|
||||
std::string Path() const;
|
||||
virtual std::string PhysicalPath() const;
|
||||
std::string GUIDString() const;
|
||||
nvlist_t *PoolConfig() const;
|
||||
nvlist_t *Config() const;
|
||||
Vdev Parent();
|
||||
Vdev RootVdev();
|
||||
std::string Name(zpool_handle_t *, bool verbose) const;
|
||||
bool IsSpare();
|
||||
bool IsAvailableSpare() const;
|
||||
bool IsActiveSpare() const;
|
||||
bool IsResilvering() const;
|
||||
|
||||
private:
|
||||
void VdevLookupGuid();
|
||||
bool VdevLookupPoolGuid();
|
||||
DevdCtl::Guid m_poolGUID;
|
||||
DevdCtl::Guid m_vdevGUID;
|
||||
nvlist_t *m_poolConfig;
|
||||
nvlist_t *m_config;
|
||||
};
|
||||
|
||||
//- Special objects -----------------------------------------------------------
|
||||
extern Vdev NonexistentVdev;
|
||||
|
||||
//- Vdev Inline Public Methods ------------------------------------------------
|
||||
inline DevdCtl::Guid
|
||||
Vdev::PoolGUID() const
|
||||
{
|
||||
return (m_poolGUID);
|
||||
}
|
||||
|
||||
inline DevdCtl::Guid
|
||||
Vdev::GUID() const
|
||||
{
|
||||
return (m_vdevGUID);
|
||||
}
|
||||
|
||||
inline nvlist_t *
|
||||
Vdev::PoolConfig() const
|
||||
{
|
||||
return (m_poolConfig);
|
||||
}
|
||||
|
||||
inline nvlist_t *
|
||||
Vdev::Config() const
|
||||
{
|
||||
return (m_config);
|
||||
}
|
||||
|
||||
inline bool
|
||||
Vdev::DoesNotExist() const
|
||||
{
|
||||
return (m_config == NULL);
|
||||
}
|
||||
|
||||
#endif /* _VDEV_H_ */
|
153
cddl/usr.sbin/zfsd/vdev_iterator.cc
Normal file
153
cddl/usr.sbin/zfsd/vdev_iterator.cc
Normal file
@ -0,0 +1,153 @@
|
||||
/*-
|
||||
* Copyright (c) 2011, 2012, 2013 Spectra Logic Corporation
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions, and the following disclaimer,
|
||||
* without modification.
|
||||
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
|
||||
* substantially similar to the "NO WARRANTY" disclaimer below
|
||||
* ("Disclaimer") and any redistribution must be conditioned upon
|
||||
* including a substantially similar Disclaimer requirement for further
|
||||
* binary redistribution.
|
||||
*
|
||||
* NO WARRANTY
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGES.
|
||||
*
|
||||
* Authors: Justin T. Gibbs (Spectra Logic Corporation)
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file vdev_iterator.cc
|
||||
*
|
||||
* Implementation of the VdevIterator class.
|
||||
*/
|
||||
#include <sys/cdefs.h>
|
||||
#include <sys/fs/zfs.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <syslog.h>
|
||||
|
||||
#include <libzfs.h>
|
||||
|
||||
#include <list>
|
||||
#include <string>
|
||||
|
||||
#include <devdctl/exception.h>
|
||||
#include <devdctl/guid.h>
|
||||
|
||||
#include "vdev.h"
|
||||
#include "vdev_iterator.h"
|
||||
#include "zfsd_exception.h"
|
||||
|
||||
/*============================ Namespace Control =============================*/
|
||||
using DevdCtl::Guid;
|
||||
|
||||
/*=========================== Class Implementations ==========================*/
|
||||
/*------------------------------- VdevIterator -------------------------------*/
|
||||
VdevIterator::VdevIterator(zpool_handle_t *pool)
|
||||
: m_poolConfig(zpool_get_config(pool, NULL))
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
VdevIterator::VdevIterator(nvlist_t *poolConfig)
|
||||
: m_poolConfig(poolConfig)
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
void
|
||||
VdevIterator::Reset()
|
||||
{
|
||||
nvlist_t *rootVdev;
|
||||
int result;
|
||||
|
||||
result = nvlist_lookup_nvlist(m_poolConfig,
|
||||
ZPOOL_CONFIG_VDEV_TREE,
|
||||
&rootVdev);
|
||||
if (result != 0)
|
||||
throw ZfsdException(m_poolConfig, "Unable to extract "
|
||||
"ZPOOL_CONFIG_VDEV_TREE from pool.");
|
||||
m_vdevQueue.assign(1, rootVdev);
|
||||
}
|
||||
|
||||
nvlist_t *
|
||||
VdevIterator::Next()
|
||||
{
|
||||
nvlist_t *vdevConfig;
|
||||
|
||||
if (m_vdevQueue.empty())
|
||||
return (NULL);
|
||||
|
||||
for (;;) {
|
||||
nvlist_t **vdevChildren;
|
||||
int result;
|
||||
u_int numChildren;
|
||||
|
||||
vdevConfig = m_vdevQueue.front();
|
||||
m_vdevQueue.pop_front();
|
||||
|
||||
/* Expand non-leaf vdevs. */
|
||||
result = nvlist_lookup_nvlist_array(vdevConfig,
|
||||
ZPOOL_CONFIG_CHILDREN,
|
||||
&vdevChildren, &numChildren);
|
||||
if (result != 0) {
|
||||
/* leaf vdev */
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Insert children at the head of the queue to effect a
|
||||
* depth first traversal of the tree.
|
||||
*/
|
||||
m_vdevQueue.insert(m_vdevQueue.begin(), vdevChildren,
|
||||
vdevChildren + numChildren);
|
||||
};
|
||||
|
||||
return (vdevConfig);
|
||||
}
|
||||
|
||||
void
|
||||
VdevIterator::Each(VdevCallback_t *callBack, void *callBackArg)
|
||||
{
|
||||
nvlist_t *vdevConfig;
|
||||
|
||||
Reset();
|
||||
while ((vdevConfig = Next()) != NULL) {
|
||||
Vdev vdev(m_poolConfig, vdevConfig);
|
||||
|
||||
if (callBack(vdev, callBackArg))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
nvlist_t *
|
||||
VdevIterator::Find(Guid vdevGUID)
|
||||
{
|
||||
nvlist_t *vdevConfig;
|
||||
|
||||
Reset();
|
||||
while ((vdevConfig = Next()) != NULL) {
|
||||
Vdev vdev(m_poolConfig, vdevConfig);
|
||||
|
||||
if (vdev.GUID() == vdevGUID)
|
||||
return (vdevConfig);
|
||||
}
|
||||
return (NULL);
|
||||
}
|
123
cddl/usr.sbin/zfsd/vdev_iterator.h
Normal file
123
cddl/usr.sbin/zfsd/vdev_iterator.h
Normal file
@ -0,0 +1,123 @@
|
||||
/*-
|
||||
* Copyright (c) 2011, 2012, 2013 Spectra Logic Corporation
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions, and the following disclaimer,
|
||||
* without modification.
|
||||
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
|
||||
* substantially similar to the "NO WARRANTY" disclaimer below
|
||||
* ("Disclaimer") and any redistribution must be conditioned upon
|
||||
* including a substantially similar Disclaimer requirement for further
|
||||
* binary redistribution.
|
||||
*
|
||||
* NO WARRANTY
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGES.
|
||||
*
|
||||
* Authors: Justin T. Gibbs (Spectra Logic Corporation)
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file vdev_iterator.h
|
||||
*
|
||||
* VdevIterator class definition.
|
||||
*
|
||||
* Header requirements:
|
||||
*
|
||||
* #include <list>
|
||||
*/
|
||||
#ifndef _VDEV_ITERATOR_H_
|
||||
#define _VDEV_ITERATOR_H_
|
||||
|
||||
/*=========================== Forward Declarations ===========================*/
|
||||
struct zpool_handle;
|
||||
typedef struct zpool_handle zpool_handle_t;
|
||||
|
||||
struct nvlist;
|
||||
typedef struct nvlist nvlist_t;
|
||||
|
||||
class Vdev;
|
||||
|
||||
/*============================= Class Definitions ============================*/
|
||||
/*------------------------------- VdevIterator -------------------------------*/
|
||||
typedef bool VdevCallback_t(Vdev &vdev, void *cbArg);
|
||||
|
||||
/**
|
||||
* \brief VdevIterator provides mechanisms for traversing and searching
|
||||
* the leaf vdevs contained in a ZFS pool configuration.
|
||||
*/
|
||||
class VdevIterator
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* \brief Instantiate a VdevIterator for the given ZFS pool.
|
||||
*
|
||||
* \param pool The ZFS pool to traverse/search.
|
||||
*/
|
||||
VdevIterator(zpool_handle_t *pool);
|
||||
|
||||
/**
|
||||
* \brief Instantiate a VdevIterator for the given ZFS pool.
|
||||
*
|
||||
* \param poolConfig The configuration data for the ZFS pool
|
||||
* to traverse/search.
|
||||
*/
|
||||
VdevIterator(nvlist_t *poolConfig);
|
||||
|
||||
/**
|
||||
* \brief Reset this iterator's cursor so that Next() will
|
||||
* report the first member of the pool.
|
||||
*/
|
||||
void Reset();
|
||||
|
||||
/**
|
||||
* \brief Report the leaf vdev at this iterator's cursor and increment
|
||||
* the cursor to the next leaf pool member.
|
||||
*/
|
||||
nvlist_t *Next();
|
||||
|
||||
/**
|
||||
* \brief Traverse the entire pool configuration starting its
|
||||
* first member, returning a vdev object with the given
|
||||
* vdev GUID if found.
|
||||
*
|
||||
* \param vdevGUID The vdev GUID of the vdev object to find.
|
||||
*
|
||||
* \return A Vdev object for the matching vdev if found. Otherwise
|
||||
* NULL.
|
||||
*
|
||||
* Upon return, the VdevIterator's cursor points to the vdev just
|
||||
* past the returned vdev or end() if no matching vdev is found.
|
||||
*/
|
||||
nvlist_t *Find(DevdCtl::Guid vdevGUID);
|
||||
|
||||
/**
|
||||
* \brief Perform the specified operation on each leaf member of
|
||||
* a pool's vdev membership.
|
||||
*
|
||||
* \param cb Callback function to execute for each member.
|
||||
* \param cbArg Argument to pass to cb.
|
||||
*/
|
||||
void Each(VdevCallback_t *cb, void *cbArg);
|
||||
|
||||
private:
|
||||
nvlist_t *m_poolConfig;
|
||||
std::list<nvlist_t *> m_vdevQueue;
|
||||
};
|
||||
|
||||
#endif /* _VDEV_ITERATOR_H_ */
|
157
cddl/usr.sbin/zfsd/zfsd.8
Normal file
157
cddl/usr.sbin/zfsd/zfsd.8
Normal file
@ -0,0 +1,157 @@
|
||||
.\"-
|
||||
.\" Copyright (c) 2016 Allan Jude
|
||||
.\" All rights reserved.
|
||||
.\"
|
||||
.\" Redistribution and use in source and binary forms, with or without
|
||||
.\" modification, are permitted provided that the following conditions
|
||||
.\" are met:
|
||||
.\" 1. Redistributions of source code must retain the above copyright
|
||||
.\" notice, this list of conditions and the following disclaimer.
|
||||
.\" 2. Redistributions in binary form must reproduce the above copyright
|
||||
.\" notice, this list of conditions and the following disclaimer in the
|
||||
.\" documentation and/or other materials provided with the distribution.
|
||||
.\"
|
||||
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
.\" SUCH DAMAGE.
|
||||
.\"
|
||||
.\" $FreeBSD$
|
||||
.\"
|
||||
.Dd May 26, 2016
|
||||
.Dt ZFSD 8
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm zfsd
|
||||
.Nd ZFS fault management daemon
|
||||
.Sh SYNOPSIS
|
||||
.Nm
|
||||
.Op Fl d
|
||||
.Sh DESCRIPTION
|
||||
.Nm
|
||||
attempts to resolve ZFS faults that the kernel can't resolve by itself.
|
||||
It listens to
|
||||
.Xr devctl 4
|
||||
events, which are how the kernel notifies userland of events such as I/O
|
||||
errors and disk removals.
|
||||
.Nm
|
||||
attempts to resolve these faults by activating or deactivating hot spares
|
||||
and onlining offline vdevs.
|
||||
.Pp
|
||||
The following options are available:
|
||||
.Bl -tag -width indent
|
||||
.It Fl d
|
||||
Run in the foreground instead of daemonizing.
|
||||
.El
|
||||
.Pp
|
||||
System administrators never interact with
|
||||
.Nm
|
||||
directly.
|
||||
Instead, they control its behavior indirectly through zpool configuration.
|
||||
There are two ways to influence
|
||||
.Nm :
|
||||
assigning hotspares and setting pool properties.
|
||||
Currently, only the
|
||||
.Em autoreplace
|
||||
property has any effect.
|
||||
See
|
||||
.Xr zpool 8
|
||||
for details.
|
||||
.Pp
|
||||
.Nm
|
||||
will attempt to resolve the following types of fault:
|
||||
.Bl -tag -width a
|
||||
.It device removal
|
||||
When a leaf vdev disappears,
|
||||
.Nm
|
||||
will activate any available hotspare.
|
||||
.It device arrival
|
||||
When a new GEOM device appears,
|
||||
.Nm
|
||||
will attempt to read its ZFS label, if any.
|
||||
If it matches a previously removed vdev on an active pool,
|
||||
.Nm
|
||||
will online it.
|
||||
Once resilvering completes, any active hotspare will detach automatically.
|
||||
.Pp
|
||||
If the new device has no ZFS label but its physical path matches the
|
||||
physical path of a previously removed vdev on an active pool, and that
|
||||
pool has the autoreplace property set, then
|
||||
.Nm
|
||||
will replace the missing vdev with the newly arrived device.
|
||||
Once resilvering completes, any active hotspare will detach automatically.
|
||||
.It vdev degrade or fault events
|
||||
If a vdev becomes degraded or faulted,
|
||||
.Nm
|
||||
will activate any available hotspare.
|
||||
.It I/O errors
|
||||
If a leaf vdev generates more than 50 I/O errors in a 60 second period, then
|
||||
.Nm
|
||||
will mark that vdev as
|
||||
.Em FAULTED .
|
||||
.Xr zfs 4
|
||||
will no longer issue any I/Os to it.
|
||||
.Nm
|
||||
will activate a hotspare if one is available.
|
||||
.It Checksum errors
|
||||
If a leaf vdev generates more than 50 checksum errors in a 60 second
|
||||
period, then
|
||||
.Nm
|
||||
will mark that vdev as
|
||||
.Em DEGRADED .
|
||||
.Xr zfs 4
|
||||
will still use it, but zfsd will activate a spare anyway.
|
||||
.It Spare addition
|
||||
If the system administrator adds a hotspare to a pool that is already degraded,
|
||||
.Nm
|
||||
will activate the spare.
|
||||
.It Resilver complete
|
||||
.Nm
|
||||
will detach any hotspare once a permanent replacement finishes resilvering.
|
||||
.It Physical path change
|
||||
If the physical path of an existing disk changes,
|
||||
.Nm
|
||||
will attempt to replace any missing disk with the same physical path,
|
||||
if its pool's autoreplace property is set.
|
||||
.El
|
||||
.Pp
|
||||
.Nm
|
||||
will log interesting events and its actions to syslog with facility
|
||||
.Em daemon
|
||||
and identity
|
||||
.Op zfsd .
|
||||
.El
|
||||
.Sh FILES
|
||||
.Bl -tag -width a -compact
|
||||
.It Pa /var/db/zfsd/cases
|
||||
When
|
||||
.Nm
|
||||
exits, it serializes any unresolved casefiles here,
|
||||
then reads them back in when next it starts up.
|
||||
.El
|
||||
.Sh SEE ALSO
|
||||
.Xr devctl 4 ,
|
||||
.Xr zfs 4 ,
|
||||
.Xr zpool 8
|
||||
.Sh HISTORY
|
||||
.Nm
|
||||
first appeared in
|
||||
.Fx 11.0 .
|
||||
.Sh AUTHORS
|
||||
.Nm
|
||||
was originally written by
|
||||
.An Justin Gibbs Aq Mt gibbs@FreeBSD.org
|
||||
and
|
||||
.An Alan Somers Aq Mt asomers@FreeBSD.org
|
||||
.Sh TODO
|
||||
In the future,
|
||||
.Nm
|
||||
should be able to resume a pool that became suspended due to device
|
||||
removals, if enough missing devices have returned.
|
448
cddl/usr.sbin/zfsd/zfsd.cc
Normal file
448
cddl/usr.sbin/zfsd/zfsd.cc
Normal file
@ -0,0 +1,448 @@
|
||||
/*-
|
||||
* Copyright (c) 2011, 2012, 2013, 2014, 2015, 2016 Spectra Logic Corporation
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions, and the following disclaimer,
|
||||
* without modification.
|
||||
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
|
||||
* substantially similar to the "NO WARRANTY" disclaimer below
|
||||
* ("Disclaimer") and any redistribution must be conditioned upon
|
||||
* including a substantially similar Disclaimer requirement for further
|
||||
* binary redistribution.
|
||||
*
|
||||
* NO WARRANTY
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGES.
|
||||
*
|
||||
* Authors: Justin T. Gibbs (Spectra Logic Corporation)
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file zfsd.cc
|
||||
*
|
||||
* The ZFS daemon consumes kernel devdctl(4) event data via devd(8)'s
|
||||
* unix domain socket in order to react to system changes that impact
|
||||
* the function of ZFS storage pools. The goal of this daemon is to
|
||||
* provide similar functionality to the Solaris ZFS Diagnostic Engine
|
||||
* (zfs-diagnosis), the Solaris ZFS fault handler (zfs-retire), and
|
||||
* the Solaris ZFS vdev insertion agent (zfs-mod sysevent handler).
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/fs/zfs.h>
|
||||
|
||||
#include <err.h>
|
||||
#include <libgeom.h>
|
||||
#include <libutil.h>
|
||||
#include <poll.h>
|
||||
#include <syslog.h>
|
||||
|
||||
#include <libzfs.h>
|
||||
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include <devdctl/guid.h>
|
||||
#include <devdctl/event.h>
|
||||
#include <devdctl/event_factory.h>
|
||||
#include <devdctl/exception.h>
|
||||
#include <devdctl/consumer.h>
|
||||
|
||||
#include "callout.h"
|
||||
#include "vdev_iterator.h"
|
||||
#include "zfsd_event.h"
|
||||
#include "case_file.h"
|
||||
#include "vdev.h"
|
||||
#include "vdev_iterator.h"
|
||||
#include "zfsd.h"
|
||||
#include "zfsd_exception.h"
|
||||
#include "zpool_list.h"
|
||||
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
/*================================== Macros ==================================*/
|
||||
#define NUM_ELEMENTS(x) (sizeof(x) / sizeof(*x))
|
||||
|
||||
/*============================ Namespace Control =============================*/
|
||||
using DevdCtl::Event;
|
||||
using DevdCtl::EventFactory;
|
||||
using DevdCtl::EventList;
|
||||
|
||||
/*================================ Global Data ===============================*/
|
||||
int g_debug = 0;
|
||||
libzfs_handle_t *g_zfsHandle;
|
||||
|
||||
/*--------------------------------- ZfsDaemon --------------------------------*/
|
||||
//- ZfsDaemon Static Private Data ----------------------------------------------
|
||||
ZfsDaemon *ZfsDaemon::s_theZfsDaemon;
|
||||
bool ZfsDaemon::s_logCaseFiles;
|
||||
bool ZfsDaemon::s_terminateEventLoop;
|
||||
char ZfsDaemon::s_pidFilePath[] = "/var/run/zfsd.pid";
|
||||
pidfh *ZfsDaemon::s_pidFH;
|
||||
int ZfsDaemon::s_signalPipeFD[2];
|
||||
bool ZfsDaemon::s_systemRescanRequested(false);
|
||||
EventFactory::Record ZfsDaemon::s_registryEntries[] =
|
||||
{
|
||||
{ Event::NOTIFY, "DEVFS", &DevfsEvent::Builder },
|
||||
{ Event::NOTIFY, "GEOM", &GeomEvent::Builder },
|
||||
{ Event::NOTIFY, "ZFS", &ZfsEvent::Builder }
|
||||
};
|
||||
|
||||
//- ZfsDaemon Static Public Methods --------------------------------------------
|
||||
ZfsDaemon &
|
||||
ZfsDaemon::Get()
|
||||
{
|
||||
return (*s_theZfsDaemon);
|
||||
}
|
||||
|
||||
void
|
||||
ZfsDaemon::WakeEventLoop()
|
||||
{
|
||||
write(s_signalPipeFD[1], "+", 1);
|
||||
}
|
||||
|
||||
void
|
||||
ZfsDaemon::RequestSystemRescan()
|
||||
{
|
||||
s_systemRescanRequested = true;
|
||||
ZfsDaemon::WakeEventLoop();
|
||||
}
|
||||
|
||||
void
|
||||
ZfsDaemon::Run()
|
||||
{
|
||||
ZfsDaemon daemon;
|
||||
|
||||
while (s_terminateEventLoop == false) {
|
||||
|
||||
try {
|
||||
daemon.DisconnectFromDevd();
|
||||
|
||||
if (daemon.ConnectToDevd() == false) {
|
||||
sleep(30);
|
||||
continue;
|
||||
}
|
||||
|
||||
daemon.DetectMissedEvents();
|
||||
|
||||
daemon.EventLoop();
|
||||
|
||||
} catch (const DevdCtl::Exception &exp) {
|
||||
exp.Log();
|
||||
}
|
||||
}
|
||||
|
||||
daemon.DisconnectFromDevd();
|
||||
}
|
||||
|
||||
//- ZfsDaemon Private Methods --------------------------------------------------
|
||||
ZfsDaemon::ZfsDaemon()
|
||||
: Consumer(/*defBuilder*/NULL, s_registryEntries,
|
||||
NUM_ELEMENTS(s_registryEntries))
|
||||
{
|
||||
if (s_theZfsDaemon != NULL)
|
||||
errx(1, "Multiple ZfsDaemon instances created. Exiting");
|
||||
|
||||
s_theZfsDaemon = this;
|
||||
|
||||
if (pipe(s_signalPipeFD) != 0)
|
||||
errx(1, "Unable to allocate signal pipe. Exiting");
|
||||
|
||||
if (fcntl(s_signalPipeFD[0], F_SETFL, O_NONBLOCK) == -1)
|
||||
errx(1, "Unable to set pipe as non-blocking. Exiting");
|
||||
|
||||
if (fcntl(s_signalPipeFD[1], F_SETFL, O_NONBLOCK) == -1)
|
||||
errx(1, "Unable to set pipe as non-blocking. Exiting");
|
||||
|
||||
signal(SIGHUP, ZfsDaemon::RescanSignalHandler);
|
||||
signal(SIGINFO, ZfsDaemon::InfoSignalHandler);
|
||||
signal(SIGINT, ZfsDaemon::QuitSignalHandler);
|
||||
signal(SIGTERM, ZfsDaemon::QuitSignalHandler);
|
||||
signal(SIGUSR1, ZfsDaemon::RescanSignalHandler);
|
||||
|
||||
g_zfsHandle = libzfs_init();
|
||||
if (g_zfsHandle == NULL)
|
||||
errx(1, "Unable to initialize ZFS library. Exiting");
|
||||
|
||||
Callout::Init();
|
||||
InitializeSyslog();
|
||||
OpenPIDFile();
|
||||
|
||||
if (g_debug == 0)
|
||||
daemon(0, 0);
|
||||
|
||||
UpdatePIDFile();
|
||||
}
|
||||
|
||||
ZfsDaemon::~ZfsDaemon()
|
||||
{
|
||||
PurgeCaseFiles();
|
||||
ClosePIDFile();
|
||||
}
|
||||
|
||||
void
|
||||
ZfsDaemon::PurgeCaseFiles()
|
||||
{
|
||||
CaseFile::PurgeAll();
|
||||
}
|
||||
|
||||
bool
|
||||
ZfsDaemon::VdevAddCaseFile(Vdev &vdev, void *cbArg)
|
||||
{
|
||||
if (vdev.State() != VDEV_STATE_HEALTHY)
|
||||
CaseFile::Create(vdev);
|
||||
|
||||
return (/*break early*/false);
|
||||
}
|
||||
|
||||
void
|
||||
ZfsDaemon::BuildCaseFiles()
|
||||
{
|
||||
ZpoolList zpl;
|
||||
ZpoolList::iterator pool;
|
||||
|
||||
/* Add CaseFiles for vdevs with issues. */
|
||||
for (pool = zpl.begin(); pool != zpl.end(); pool++)
|
||||
VdevIterator(*pool).Each(VdevAddCaseFile, NULL);
|
||||
|
||||
/* De-serialize any saved cases. */
|
||||
CaseFile::DeSerialize();
|
||||
|
||||
/* Simulate config_sync events to force CaseFile reevaluation */
|
||||
for (pool = zpl.begin(); pool != zpl.end(); pool++) {
|
||||
char evString[160];
|
||||
Event *event;
|
||||
nvlist_t *config;
|
||||
uint64_t poolGUID;
|
||||
const char *poolname;
|
||||
|
||||
poolname = zpool_get_name(*pool);
|
||||
config = zpool_get_config(*pool, NULL);
|
||||
if (config == NULL) {
|
||||
syslog(LOG_ERR, "ZFSDaemon::BuildCaseFiles: Could not "
|
||||
"find pool config for pool %s", poolname);
|
||||
continue;
|
||||
}
|
||||
if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID,
|
||||
&poolGUID) != 0) {
|
||||
syslog(LOG_ERR, "ZFSDaemon::BuildCaseFiles: Could not "
|
||||
"find pool guid for pool %s", poolname);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
snprintf(evString, 160, "!system=ZFS subsystem=ZFS "
|
||||
"type=misc.fs.zfs.config_sync sub_type=synthesized "
|
||||
"pool_name=%s pool_guid=%" PRIu64 "\n", poolname, poolGUID);
|
||||
event = Event::CreateEvent(GetFactory(), string(evString));
|
||||
if (event != NULL) {
|
||||
event->Process();
|
||||
delete event;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ZfsDaemon::RescanSystem()
|
||||
{
|
||||
struct gmesh mesh;
|
||||
struct gclass *mp;
|
||||
struct ggeom *gp;
|
||||
struct gprovider *pp;
|
||||
int result;
|
||||
|
||||
/*
|
||||
* The devdctl system doesn't replay events for new consumers
|
||||
* of the interface. Emit manufactured DEVFS arrival events
|
||||
* for any devices that already before we started or during
|
||||
* periods where we've lost our connection to devd.
|
||||
*/
|
||||
result = geom_gettree(&mesh);
|
||||
if (result != 0) {
|
||||
syslog(LOG_ERR, "ZfsDaemon::RescanSystem: "
|
||||
"geom_gettree faild with error %d\n", result);
|
||||
return;
|
||||
}
|
||||
|
||||
const string evStart("!system=DEVFS subsystem=CDEV type=CREATE "
|
||||
"sub_type=synthesized cdev=");
|
||||
LIST_FOREACH(mp, &mesh.lg_class, lg_class) {
|
||||
LIST_FOREACH(gp, &mp->lg_geom, lg_geom) {
|
||||
LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
|
||||
Event *event;
|
||||
|
||||
string evString(evStart + pp->lg_name + "\n");
|
||||
event = Event::CreateEvent(GetFactory(),
|
||||
evString);
|
||||
if (event != NULL) {
|
||||
if (event->Process())
|
||||
SaveEvent(*event);
|
||||
delete event;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
geom_deletetree(&mesh);
|
||||
}
|
||||
|
||||
void
|
||||
ZfsDaemon::DetectMissedEvents()
|
||||
{
|
||||
do {
|
||||
PurgeCaseFiles();
|
||||
|
||||
/*
|
||||
* Discard any events waiting for us. We don't know
|
||||
* if they still apply to the current state of the
|
||||
* system.
|
||||
*/
|
||||
FlushEvents();
|
||||
|
||||
BuildCaseFiles();
|
||||
|
||||
/*
|
||||
* If the system state has changed during our
|
||||
* interrogation, start over.
|
||||
*/
|
||||
} while (s_terminateEventLoop == false && EventsPending());
|
||||
|
||||
RescanSystem();
|
||||
}
|
||||
|
||||
void
|
||||
ZfsDaemon::EventLoop()
|
||||
{
|
||||
while (s_terminateEventLoop == false) {
|
||||
struct pollfd fds[2];
|
||||
int result;
|
||||
|
||||
if (s_logCaseFiles == true) {
|
||||
EventList::iterator event(m_unconsumedEvents.begin());
|
||||
s_logCaseFiles = false;
|
||||
CaseFile::LogAll();
|
||||
while (event != m_unconsumedEvents.end())
|
||||
(*event++)->Log(LOG_INFO);
|
||||
}
|
||||
|
||||
Callout::ExpireCallouts();
|
||||
|
||||
/* Wait for data. */
|
||||
fds[0].fd = m_devdSockFD;
|
||||
fds[0].events = POLLIN;
|
||||
fds[0].revents = 0;
|
||||
fds[1].fd = s_signalPipeFD[0];
|
||||
fds[1].events = POLLIN;
|
||||
fds[1].revents = 0;
|
||||
result = poll(fds, NUM_ELEMENTS(fds), /*timeout*/INFTIM);
|
||||
if (result == -1) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
else
|
||||
err(1, "Polling for devd events failed");
|
||||
} else if (result == 0) {
|
||||
errx(1, "Unexpected result of 0 from poll. Exiting");
|
||||
}
|
||||
|
||||
if ((fds[0].revents & POLLIN) != 0)
|
||||
ProcessEvents();
|
||||
|
||||
if ((fds[1].revents & POLLIN) != 0) {
|
||||
static char discardBuf[128];
|
||||
|
||||
/*
|
||||
* This pipe exists just to close the signal
|
||||
* race. Its contents are of no interest to
|
||||
* us, but we must ensure that future signals
|
||||
* have space in the pipe to write.
|
||||
*/
|
||||
while (read(s_signalPipeFD[0], discardBuf,
|
||||
sizeof(discardBuf)) > 0)
|
||||
;
|
||||
}
|
||||
|
||||
if (s_systemRescanRequested == true) {
|
||||
s_systemRescanRequested = false;
|
||||
syslog(LOG_INFO, "System Rescan request processed.");
|
||||
RescanSystem();
|
||||
}
|
||||
|
||||
if ((fds[0].revents & POLLERR) != 0) {
|
||||
syslog(LOG_INFO, "POLLERROR detected on devd socket.");
|
||||
break;
|
||||
}
|
||||
|
||||
if ((fds[0].revents & POLLHUP) != 0) {
|
||||
syslog(LOG_INFO, "POLLHUP detected on devd socket.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
//- ZfsDaemon staic Private Methods --------------------------------------------
|
||||
void
|
||||
ZfsDaemon::InfoSignalHandler(int)
|
||||
{
|
||||
s_logCaseFiles = true;
|
||||
ZfsDaemon::WakeEventLoop();
|
||||
}
|
||||
|
||||
void
|
||||
ZfsDaemon::RescanSignalHandler(int)
|
||||
{
|
||||
RequestSystemRescan();
|
||||
}
|
||||
|
||||
void
|
||||
ZfsDaemon::QuitSignalHandler(int)
|
||||
{
|
||||
s_terminateEventLoop = true;
|
||||
ZfsDaemon::WakeEventLoop();
|
||||
}
|
||||
|
||||
void
|
||||
ZfsDaemon::OpenPIDFile()
|
||||
{
|
||||
pid_t otherPID;
|
||||
|
||||
s_pidFH = pidfile_open(s_pidFilePath, 0600, &otherPID);
|
||||
if (s_pidFH == NULL) {
|
||||
if (errno == EEXIST)
|
||||
errx(1, "already running as PID %d. Exiting", otherPID);
|
||||
warn("cannot open PID file");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ZfsDaemon::UpdatePIDFile()
|
||||
{
|
||||
if (s_pidFH != NULL)
|
||||
pidfile_write(s_pidFH);
|
||||
}
|
||||
|
||||
void
|
||||
ZfsDaemon::ClosePIDFile()
|
||||
{
|
||||
if (s_pidFH != NULL)
|
||||
pidfile_close(s_pidFH);
|
||||
}
|
||||
|
||||
void
|
||||
ZfsDaemon::InitializeSyslog()
|
||||
{
|
||||
openlog("zfsd", LOG_NDELAY, LOG_DAEMON);
|
||||
}
|
||||
|
228
cddl/usr.sbin/zfsd/zfsd.h
Normal file
228
cddl/usr.sbin/zfsd/zfsd.h
Normal file
@ -0,0 +1,228 @@
|
||||
/*-
|
||||
* Copyright (c) 2011, 2012, 2013, 2014 Spectra Logic Corporation
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions, and the following disclaimer,
|
||||
* without modification.
|
||||
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
|
||||
* substantially similar to the "NO WARRANTY" disclaimer below
|
||||
* ("Disclaimer") and any redistribution must be conditioned upon
|
||||
* including a substantially similar Disclaimer requirement for further
|
||||
* binary redistribution.
|
||||
*
|
||||
* NO WARRANTY
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGES.
|
||||
*
|
||||
* Authors: Justin T. Gibbs (Spectra Logic Corporation)
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file zfsd.h
|
||||
*
|
||||
* Class definitions and supporting data strutures for the ZFS fault
|
||||
* management daemon.
|
||||
*
|
||||
* Header requirements:
|
||||
*
|
||||
* #include <sys/fs/zfs.h>
|
||||
*
|
||||
* #include <libzfs.h>
|
||||
*
|
||||
* #include <list>
|
||||
* #include <map>
|
||||
* #include <string>
|
||||
*
|
||||
* #include <devdctl/guid.h>
|
||||
* #include <devdctl/event.h>
|
||||
* #include <devdctl/event_factory.h>
|
||||
* #include <devdctl/consumer.h>
|
||||
*
|
||||
* #include "vdev_iterator.h"
|
||||
*/
|
||||
#ifndef _ZFSD_H_
|
||||
#define _ZFSD_H_
|
||||
|
||||
/*=========================== Forward Declarations ===========================*/
|
||||
struct pidfh;
|
||||
|
||||
struct zpool_handle;
|
||||
typedef struct zpool_handle zpool_handle_t;
|
||||
|
||||
struct zfs_handle;
|
||||
typedef struct libzfs_handle libzfs_handle_t;
|
||||
|
||||
struct nvlist;
|
||||
typedef struct nvlist nvlist_t;
|
||||
|
||||
typedef int LeafIterFunc(zpool_handle_t *, nvlist_t *, void *);
|
||||
|
||||
/*================================ Global Data ===============================*/
|
||||
extern int g_debug;
|
||||
extern libzfs_handle_t *g_zfsHandle;
|
||||
|
||||
/*============================= Class Definitions ============================*/
|
||||
/*--------------------------------- ZfsDaemon --------------------------------*/
|
||||
/**
|
||||
* Static singleton orchestrating the operations of the ZFS daemon program.
|
||||
*/
|
||||
class ZfsDaemon : public DevdCtl::Consumer
|
||||
{
|
||||
public:
|
||||
/** Return the ZfsDaemon singleton. */
|
||||
static ZfsDaemon &Get();
|
||||
|
||||
/**
|
||||
* Used by signal handlers to ensure, in a race free way, that
|
||||
* the event loop will perform at least one more full loop
|
||||
* before sleeping again.
|
||||
*/
|
||||
static void WakeEventLoop();
|
||||
|
||||
/**
|
||||
* Schedules a rescan of devices in the system for potential
|
||||
* candidates to replace a missing vdev. The scan is performed
|
||||
* during the next run of the event loop.
|
||||
*/
|
||||
static void RequestSystemRescan();
|
||||
|
||||
/** Daemonize and perform all functions of the ZFS daemon. */
|
||||
static void Run();
|
||||
|
||||
private:
|
||||
ZfsDaemon();
|
||||
~ZfsDaemon();
|
||||
|
||||
static VdevCallback_t VdevAddCaseFile;
|
||||
|
||||
/** Purge our cache of outstanding ZFS issues in the system. */
|
||||
void PurgeCaseFiles();
|
||||
|
||||
/** Build a cache of outstanding ZFS issues in the system. */
|
||||
void BuildCaseFiles();
|
||||
|
||||
/**
|
||||
* Iterate over all known issues and attempt to solve them
|
||||
* given resources currently available in the system.
|
||||
*/
|
||||
void RescanSystem();
|
||||
|
||||
/**
|
||||
* Interrogate the system looking for previously unknown
|
||||
* faults that occurred either before ZFSD was started,
|
||||
* or during a period of lost communication with Devd.
|
||||
*/
|
||||
void DetectMissedEvents();
|
||||
|
||||
/**
|
||||
* Wait for and process event source activity.
|
||||
*/
|
||||
void EventLoop();
|
||||
|
||||
/**
|
||||
* Signal handler for which our response is to
|
||||
* log the current state of the daemon.
|
||||
*
|
||||
* \param sigNum The signal caught.
|
||||
*/
|
||||
static void InfoSignalHandler(int sigNum);
|
||||
|
||||
/**
|
||||
* Signal handler for which our response is to
|
||||
* request a case rescan.
|
||||
*
|
||||
* \param sigNum The signal caught.
|
||||
*/
|
||||
static void RescanSignalHandler(int sigNum);
|
||||
|
||||
/**
|
||||
* Signal handler for which our response is to
|
||||
* gracefully terminate.
|
||||
*
|
||||
* \param sigNum The signal caught.
|
||||
*/
|
||||
static void QuitSignalHandler(int sigNum);
|
||||
|
||||
/**
|
||||
* Open and lock our PID file.
|
||||
*/
|
||||
static void OpenPIDFile();
|
||||
|
||||
/**
|
||||
* Update our PID file with our PID.
|
||||
*/
|
||||
static void UpdatePIDFile();
|
||||
|
||||
/**
|
||||
* Close and release the lock on our PID file.
|
||||
*/
|
||||
static void ClosePIDFile();
|
||||
|
||||
/**
|
||||
* Perform syslog configuration.
|
||||
*/
|
||||
static void InitializeSyslog();
|
||||
|
||||
static ZfsDaemon *s_theZfsDaemon;
|
||||
|
||||
/**
|
||||
* Set to true when our program is signaled to
|
||||
* gracefully exit.
|
||||
*/
|
||||
static bool s_logCaseFiles;
|
||||
|
||||
/**
|
||||
* Set to true when our program is signaled to
|
||||
* gracefully exit.
|
||||
*/
|
||||
static bool s_terminateEventLoop;
|
||||
|
||||
/**
|
||||
* The canonical path and file name of zfsd's PID file.
|
||||
*/
|
||||
static char s_pidFilePath[];
|
||||
|
||||
/**
|
||||
* Control structure for PIDFILE(3) API.
|
||||
*/
|
||||
static pidfh *s_pidFH;
|
||||
|
||||
/**
|
||||
* Pipe file descriptors used to close races with our
|
||||
* signal handlers.
|
||||
*/
|
||||
static int s_signalPipeFD[2];
|
||||
|
||||
/**
|
||||
* Flag controlling a rescan from ZFSD's event loop of all
|
||||
* GEOM providers in the system to find candidates for solving
|
||||
* cases.
|
||||
*/
|
||||
static bool s_systemRescanRequested;
|
||||
|
||||
/**
|
||||
* Flag controlling whether events can be queued. This boolean
|
||||
* is set during event replay to ensure that events for pools or
|
||||
* devices no longer in the system are not retained forever.
|
||||
*/
|
||||
static bool s_consumingEvents;
|
||||
|
||||
static DevdCtl::EventFactory::Record s_registryEntries[];
|
||||
};
|
||||
|
||||
#endif /* _ZFSD_H_ */
|
535
cddl/usr.sbin/zfsd/zfsd_event.cc
Normal file
535
cddl/usr.sbin/zfsd/zfsd_event.cc
Normal file
@ -0,0 +1,535 @@
|
||||
/*-
|
||||
* Copyright (c) 2011, 2012, 2013, 2014, 2016 Spectra Logic Corporation
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions, and the following disclaimer,
|
||||
* without modification.
|
||||
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
|
||||
* substantially similar to the "NO WARRANTY" disclaimer below
|
||||
* ("Disclaimer") and any redistribution must be conditioned upon
|
||||
* including a substantially similar Disclaimer requirement for further
|
||||
* binary redistribution.
|
||||
*
|
||||
* NO WARRANTY
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGES.
|
||||
*
|
||||
* Authors: Justin T. Gibbs (Spectra Logic Corporation)
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file zfsd_event.cc
|
||||
*/
|
||||
#include <sys/cdefs.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/fs/zfs.h>
|
||||
|
||||
#include <syslog.h>
|
||||
|
||||
#include <libzfs.h>
|
||||
/*
|
||||
* Undefine flush, defined by cpufunc.h on sparc64, because it conflicts with
|
||||
* C++ flush methods
|
||||
*/
|
||||
#undef flush
|
||||
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include <devdctl/guid.h>
|
||||
#include <devdctl/event.h>
|
||||
#include <devdctl/event_factory.h>
|
||||
#include <devdctl/exception.h>
|
||||
#include <devdctl/consumer.h>
|
||||
|
||||
#include "callout.h"
|
||||
#include "vdev_iterator.h"
|
||||
#include "zfsd_event.h"
|
||||
#include "case_file.h"
|
||||
#include "vdev.h"
|
||||
#include "zfsd.h"
|
||||
#include "zfsd_exception.h"
|
||||
#include "zpool_list.h"
|
||||
|
||||
__FBSDID("$FreeBSD$");
|
||||
/*============================ Namespace Control =============================*/
|
||||
using DevdCtl::Event;
|
||||
using DevdCtl::Guid;
|
||||
using DevdCtl::NVPairMap;
|
||||
using std::stringstream;
|
||||
|
||||
/*=========================== Class Implementations ==========================*/
|
||||
|
||||
/*-------------------------------- DevfsEvent --------------------------------*/
|
||||
|
||||
//- DevfsEvent Static Public Methods -------------------------------------------
|
||||
Event *
|
||||
DevfsEvent::Builder(Event::Type type,
|
||||
NVPairMap &nvPairs,
|
||||
const string &eventString)
|
||||
{
|
||||
return (new DevfsEvent(type, nvPairs, eventString));
|
||||
}
|
||||
|
||||
//- DevfsEvent Static Protected Methods ----------------------------------------
|
||||
nvlist_t *
|
||||
DevfsEvent::ReadLabel(int devFd, bool &inUse, bool °raded)
|
||||
{
|
||||
pool_state_t poolState;
|
||||
char *poolName;
|
||||
boolean_t b_inuse;
|
||||
|
||||
inUse = false;
|
||||
degraded = false;
|
||||
poolName = NULL;
|
||||
if (zpool_in_use(g_zfsHandle, devFd, &poolState,
|
||||
&poolName, &b_inuse) == 0) {
|
||||
nvlist_t *devLabel;
|
||||
|
||||
inUse = b_inuse == B_TRUE;
|
||||
if (poolName != NULL)
|
||||
free(poolName);
|
||||
|
||||
if (zpool_read_label(devFd, &devLabel) != 0
|
||||
|| devLabel == NULL)
|
||||
return (NULL);
|
||||
|
||||
try {
|
||||
Vdev vdev(devLabel);
|
||||
degraded = vdev.State() != VDEV_STATE_HEALTHY;
|
||||
return (devLabel);
|
||||
} catch (ZfsdException &exp) {
|
||||
string devName = fdevname(devFd);
|
||||
string devPath = _PATH_DEV + devName;
|
||||
string context("DevfsEvent::ReadLabel: "
|
||||
+ devPath + ": ");
|
||||
|
||||
exp.GetString().insert(0, context);
|
||||
exp.Log();
|
||||
}
|
||||
}
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
bool
|
||||
DevfsEvent::OnlineByLabel(const string &devPath, const string& physPath,
|
||||
nvlist_t *devConfig)
|
||||
{
|
||||
try {
|
||||
/*
|
||||
* A device with ZFS label information has been
|
||||
* inserted. If it matches a device for which we
|
||||
* have a case, see if we can solve that case.
|
||||
*/
|
||||
syslog(LOG_INFO, "Interrogating VDEV label for %s\n",
|
||||
devPath.c_str());
|
||||
Vdev vdev(devConfig);
|
||||
CaseFile *caseFile(CaseFile::Find(vdev.PoolGUID(),
|
||||
vdev.GUID()));
|
||||
if (caseFile != NULL)
|
||||
return (caseFile->ReEvaluate(devPath, physPath, &vdev));
|
||||
|
||||
} catch (ZfsdException &exp) {
|
||||
string context("DevfsEvent::OnlineByLabel: " + devPath + ": ");
|
||||
|
||||
exp.GetString().insert(0, context);
|
||||
exp.Log();
|
||||
}
|
||||
return (false);
|
||||
}
|
||||
|
||||
//- DevfsEvent Virtual Public Methods ------------------------------------------
|
||||
Event *
|
||||
DevfsEvent::DeepCopy() const
|
||||
{
|
||||
return (new DevfsEvent(*this));
|
||||
}
|
||||
|
||||
bool
|
||||
DevfsEvent::Process() const
|
||||
{
|
||||
/*
|
||||
* We are only concerned with newly discovered
|
||||
* devices that can be ZFS vdevs.
|
||||
*/
|
||||
if (Value("type") != "CREATE" || !IsDiskDev())
|
||||
return (false);
|
||||
|
||||
/* Log the event since it is of interest. */
|
||||
Log(LOG_INFO);
|
||||
|
||||
string devPath;
|
||||
if (!DevPath(devPath))
|
||||
return (false);
|
||||
|
||||
int devFd(open(devPath.c_str(), O_RDONLY));
|
||||
if (devFd == -1)
|
||||
return (false);
|
||||
|
||||
bool inUse;
|
||||
bool degraded;
|
||||
nvlist_t *devLabel(ReadLabel(devFd, inUse, degraded));
|
||||
|
||||
string physPath;
|
||||
bool havePhysPath(PhysicalPath(physPath));
|
||||
|
||||
string devName;
|
||||
DevName(devName);
|
||||
close(devFd);
|
||||
|
||||
if (inUse && devLabel != NULL) {
|
||||
OnlineByLabel(devPath, physPath, devLabel);
|
||||
} else if (degraded) {
|
||||
syslog(LOG_INFO, "%s is marked degraded. Ignoring "
|
||||
"as a replace by physical path candidate.\n",
|
||||
devName.c_str());
|
||||
} else if (havePhysPath && IsWholeDev()) {
|
||||
/*
|
||||
* TODO: attempt to resolve events using every casefile
|
||||
* that matches this physpath
|
||||
*/
|
||||
CaseFile *caseFile(CaseFile::Find(physPath));
|
||||
if (caseFile != NULL) {
|
||||
syslog(LOG_INFO,
|
||||
"Found CaseFile(%s:%s:%s) - ReEvaluating\n",
|
||||
caseFile->PoolGUIDString().c_str(),
|
||||
caseFile->VdevGUIDString().c_str(),
|
||||
zpool_state_to_name(caseFile->VdevState(),
|
||||
VDEV_AUX_NONE));
|
||||
caseFile->ReEvaluate(devPath, physPath, /*vdev*/NULL);
|
||||
}
|
||||
}
|
||||
if (devLabel != NULL)
|
||||
nvlist_free(devLabel);
|
||||
return (false);
|
||||
}
|
||||
|
||||
//- DevfsEvent Protected Methods -----------------------------------------------
|
||||
DevfsEvent::DevfsEvent(Event::Type type, NVPairMap &nvpairs,
|
||||
const string &eventString)
|
||||
: DevdCtl::DevfsEvent(type, nvpairs, eventString)
|
||||
{
|
||||
}
|
||||
|
||||
DevfsEvent::DevfsEvent(const DevfsEvent &src)
|
||||
: DevdCtl::DevfsEvent::DevfsEvent(src)
|
||||
{
|
||||
}
|
||||
|
||||
/*-------------------------------- GeomEvent --------------------------------*/
|
||||
|
||||
//- GeomEvent Static Public Methods -------------------------------------------
|
||||
Event *
|
||||
GeomEvent::Builder(Event::Type type,
|
||||
NVPairMap &nvPairs,
|
||||
const string &eventString)
|
||||
{
|
||||
return (new GeomEvent(type, nvPairs, eventString));
|
||||
}
|
||||
|
||||
//- GeomEvent Virtual Public Methods ------------------------------------------
|
||||
Event *
|
||||
GeomEvent::DeepCopy() const
|
||||
{
|
||||
return (new GeomEvent(*this));
|
||||
}
|
||||
|
||||
bool
|
||||
GeomEvent::Process() const
|
||||
{
|
||||
/*
|
||||
* We are only concerned with physical path changes, because those can
|
||||
* be used to satisfy autoreplace operations
|
||||
*/
|
||||
if (Value("type") != "GEOM::physpath" || !IsDiskDev())
|
||||
return (false);
|
||||
|
||||
/* Log the event since it is of interest. */
|
||||
Log(LOG_INFO);
|
||||
|
||||
string devPath;
|
||||
if (!DevPath(devPath))
|
||||
return (false);
|
||||
|
||||
string physPath;
|
||||
bool havePhysPath(PhysicalPath(physPath));
|
||||
|
||||
string devName;
|
||||
DevName(devName);
|
||||
|
||||
if (havePhysPath) {
|
||||
/*
|
||||
* TODO: attempt to resolve events using every casefile
|
||||
* that matches this physpath
|
||||
*/
|
||||
CaseFile *caseFile(CaseFile::Find(physPath));
|
||||
if (caseFile != NULL) {
|
||||
syslog(LOG_INFO,
|
||||
"Found CaseFile(%s:%s:%s) - ReEvaluating\n",
|
||||
caseFile->PoolGUIDString().c_str(),
|
||||
caseFile->VdevGUIDString().c_str(),
|
||||
zpool_state_to_name(caseFile->VdevState(),
|
||||
VDEV_AUX_NONE));
|
||||
caseFile->ReEvaluate(devPath, physPath, /*vdev*/NULL);
|
||||
}
|
||||
}
|
||||
return (false);
|
||||
}
|
||||
|
||||
//- GeomEvent Protected Methods -----------------------------------------------
|
||||
GeomEvent::GeomEvent(Event::Type type, NVPairMap &nvpairs,
|
||||
const string &eventString)
|
||||
: DevdCtl::GeomEvent(type, nvpairs, eventString)
|
||||
{
|
||||
}
|
||||
|
||||
GeomEvent::GeomEvent(const GeomEvent &src)
|
||||
: DevdCtl::GeomEvent::GeomEvent(src)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/*--------------------------------- ZfsEvent ---------------------------------*/
|
||||
//- ZfsEvent Static Public Methods ---------------------------------------------
|
||||
DevdCtl::Event *
|
||||
ZfsEvent::Builder(Event::Type type, NVPairMap &nvpairs,
|
||||
const string &eventString)
|
||||
{
|
||||
return (new ZfsEvent(type, nvpairs, eventString));
|
||||
}
|
||||
|
||||
//- ZfsEvent Virtual Public Methods --------------------------------------------
|
||||
Event *
|
||||
ZfsEvent::DeepCopy() const
|
||||
{
|
||||
return (new ZfsEvent(*this));
|
||||
}
|
||||
|
||||
bool
|
||||
ZfsEvent::Process() const
|
||||
{
|
||||
string logstr("");
|
||||
|
||||
if (!Contains("class") && !Contains("type")) {
|
||||
syslog(LOG_ERR,
|
||||
"ZfsEvent::Process: Missing class or type data.");
|
||||
return (false);
|
||||
}
|
||||
|
||||
/* On config syncs, replay any queued events first. */
|
||||
if (Value("type").find("misc.fs.zfs.config_sync") == 0) {
|
||||
/*
|
||||
* Even if saved events are unconsumed the second time
|
||||
* around, drop them. Any events that still can't be
|
||||
* consumed are probably referring to vdevs or pools that
|
||||
* no longer exist.
|
||||
*/
|
||||
ZfsDaemon::Get().ReplayUnconsumedEvents(/*discard*/true);
|
||||
CaseFile::ReEvaluateByGuid(PoolGUID(), *this);
|
||||
}
|
||||
|
||||
if (Value("type").find("misc.fs.zfs.") == 0) {
|
||||
/* Configuration changes, resilver events, etc. */
|
||||
ProcessPoolEvent();
|
||||
return (false);
|
||||
}
|
||||
|
||||
if (!Contains("pool_guid") || !Contains("vdev_guid")) {
|
||||
/* Only currently interested in Vdev related events. */
|
||||
return (false);
|
||||
}
|
||||
|
||||
CaseFile *caseFile(CaseFile::Find(PoolGUID(), VdevGUID()));
|
||||
if (caseFile != NULL) {
|
||||
Log(LOG_INFO);
|
||||
syslog(LOG_INFO, "Evaluating existing case file\n");
|
||||
caseFile->ReEvaluate(*this);
|
||||
return (false);
|
||||
}
|
||||
|
||||
/* Skip events that can't be handled. */
|
||||
Guid poolGUID(PoolGUID());
|
||||
/* If there are no replicas for a pool, then it's not manageable. */
|
||||
if (Value("class").find("fs.zfs.vdev.no_replicas") == 0) {
|
||||
stringstream msg;
|
||||
msg << "No replicas available for pool " << poolGUID;
|
||||
msg << ", ignoring";
|
||||
Log(LOG_INFO);
|
||||
syslog(LOG_INFO, "%s", msg.str().c_str());
|
||||
return (false);
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a case file for this vdev, and have it
|
||||
* evaluate the event.
|
||||
*/
|
||||
ZpoolList zpl(ZpoolList::ZpoolByGUID, &poolGUID);
|
||||
if (zpl.empty()) {
|
||||
stringstream msg;
|
||||
int priority = LOG_INFO;
|
||||
msg << "ZfsEvent::Process: Event for unknown pool ";
|
||||
msg << poolGUID << " ";
|
||||
msg << "queued";
|
||||
Log(LOG_INFO);
|
||||
syslog(priority, "%s", msg.str().c_str());
|
||||
return (true);
|
||||
}
|
||||
|
||||
nvlist_t *vdevConfig = VdevIterator(zpl.front()).Find(VdevGUID());
|
||||
if (vdevConfig == NULL) {
|
||||
stringstream msg;
|
||||
int priority = LOG_INFO;
|
||||
msg << "ZfsEvent::Process: Event for unknown vdev ";
|
||||
msg << VdevGUID() << " ";
|
||||
msg << "queued";
|
||||
Log(LOG_INFO);
|
||||
syslog(priority, "%s", msg.str().c_str());
|
||||
return (true);
|
||||
}
|
||||
|
||||
Vdev vdev(zpl.front(), vdevConfig);
|
||||
caseFile = &CaseFile::Create(vdev);
|
||||
if (caseFile->ReEvaluate(*this) == false) {
|
||||
stringstream msg;
|
||||
int priority = LOG_INFO;
|
||||
msg << "ZfsEvent::Process: Unconsumed event for vdev(";
|
||||
msg << zpool_get_name(zpl.front()) << ",";
|
||||
msg << vdev.GUID() << ") ";
|
||||
msg << "queued";
|
||||
Log(LOG_INFO);
|
||||
syslog(priority, "%s", msg.str().c_str());
|
||||
return (true);
|
||||
}
|
||||
return (false);
|
||||
}
|
||||
|
||||
//- ZfsEvent Protected Methods -------------------------------------------------
|
||||
ZfsEvent::ZfsEvent(Event::Type type, NVPairMap &nvpairs,
|
||||
const string &eventString)
|
||||
: DevdCtl::ZfsEvent(type, nvpairs, eventString)
|
||||
{
|
||||
}
|
||||
|
||||
ZfsEvent::ZfsEvent(const ZfsEvent &src)
|
||||
: DevdCtl::ZfsEvent(src)
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* Sometimes the kernel won't detach a spare when it is no longer needed. This
|
||||
* can happen for example if a drive is removed, then either the pool is
|
||||
* exported or the machine is powered off, then the drive is reinserted, then
|
||||
* the machine is powered on or the pool is imported. ZFSD must detach these
|
||||
* spares itself.
|
||||
*/
|
||||
void
|
||||
ZfsEvent::CleanupSpares() const
|
||||
{
|
||||
Guid poolGUID(PoolGUID());
|
||||
ZpoolList zpl(ZpoolList::ZpoolByGUID, &poolGUID);
|
||||
if (!zpl.empty()) {
|
||||
zpool_handle_t* hdl;
|
||||
|
||||
hdl = zpl.front();
|
||||
VdevIterator(hdl).Each(TryDetach, (void*)hdl);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ZfsEvent::ProcessPoolEvent() const
|
||||
{
|
||||
bool degradedDevice(false);
|
||||
|
||||
/* The pool is destroyed. Discard any open cases */
|
||||
if (Value("type") == "misc.fs.zfs.pool_destroy") {
|
||||
Log(LOG_INFO);
|
||||
CaseFile::ReEvaluateByGuid(PoolGUID(), *this);
|
||||
return;
|
||||
}
|
||||
|
||||
CaseFile *caseFile(CaseFile::Find(PoolGUID(), VdevGUID()));
|
||||
if (caseFile != NULL) {
|
||||
if (caseFile->VdevState() != VDEV_STATE_UNKNOWN
|
||||
&& caseFile->VdevState() < VDEV_STATE_HEALTHY)
|
||||
degradedDevice = true;
|
||||
|
||||
Log(LOG_INFO);
|
||||
caseFile->ReEvaluate(*this);
|
||||
}
|
||||
else if (Value("type") == "misc.fs.zfs.resilver_finish")
|
||||
{
|
||||
/*
|
||||
* It's possible to get a resilver_finish event with no
|
||||
* corresponding casefile. For example, if a damaged pool were
|
||||
* exported, repaired, then reimported.
|
||||
*/
|
||||
Log(LOG_INFO);
|
||||
CleanupSpares();
|
||||
}
|
||||
|
||||
if (Value("type") == "misc.fs.zfs.vdev_remove"
|
||||
&& degradedDevice == false) {
|
||||
|
||||
/* See if any other cases can make use of this device. */
|
||||
Log(LOG_INFO);
|
||||
ZfsDaemon::RequestSystemRescan();
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
ZfsEvent::TryDetach(Vdev &vdev, void *cbArg)
|
||||
{
|
||||
/*
|
||||
* Outline:
|
||||
* if this device is a spare, and its parent includes one healthy,
|
||||
* non-spare child, then detach this device.
|
||||
*/
|
||||
zpool_handle_t *hdl(static_cast<zpool_handle_t*>(cbArg));
|
||||
|
||||
if (vdev.IsSpare()) {
|
||||
std::list<Vdev> siblings;
|
||||
std::list<Vdev>::iterator siblings_it;
|
||||
boolean_t cleanup = B_FALSE;
|
||||
|
||||
Vdev parent = vdev.Parent();
|
||||
siblings = parent.Children();
|
||||
|
||||
/* Determine whether the parent should be cleaned up */
|
||||
for (siblings_it = siblings.begin();
|
||||
siblings_it != siblings.end();
|
||||
siblings_it++) {
|
||||
Vdev sibling = *siblings_it;
|
||||
|
||||
if (!sibling.IsSpare() &&
|
||||
sibling.State() == VDEV_STATE_HEALTHY) {
|
||||
cleanup = B_TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (cleanup) {
|
||||
syslog(LOG_INFO, "Detaching spare vdev %s from pool %s",
|
||||
vdev.Path().c_str(), zpool_get_name(hdl));
|
||||
zpool_vdev_detach(hdl, vdev.Path().c_str());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Always return false, because there may be other spares to detach */
|
||||
return (false);
|
||||
}
|
168
cddl/usr.sbin/zfsd/zfsd_event.h
Normal file
168
cddl/usr.sbin/zfsd/zfsd_event.h
Normal file
@ -0,0 +1,168 @@
|
||||
/*-
|
||||
* Copyright (c) 2011, 2012, 2013, 2014, 2016 Spectra Logic Corporation
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions, and the following disclaimer,
|
||||
* without modification.
|
||||
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
|
||||
* substantially similar to the "NO WARRANTY" disclaimer below
|
||||
* ("Disclaimer") and any redistribution must be conditioned upon
|
||||
* including a substantially similar Disclaimer requirement for further
|
||||
* binary redistribution.
|
||||
*
|
||||
* NO WARRANTY
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGES.
|
||||
*
|
||||
* Authors: Justin T. Gibbs (Spectra Logic Corporation)
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file dev_ctl_event.h
|
||||
*
|
||||
* \brief Class hierarchy used to express events received via
|
||||
* the devdctl API.
|
||||
*
|
||||
* Header requirements:
|
||||
* #include <string>
|
||||
* #include <list>
|
||||
* #include <map>
|
||||
*
|
||||
* #include <devdctl/guid.h>
|
||||
* #include <devdctl/event.h>
|
||||
*/
|
||||
|
||||
#ifndef _ZFSD_EVENT_H_
|
||||
#define _ZFSD_EVENT_H_
|
||||
|
||||
/*============================ Namespace Control =============================*/
|
||||
using std::string;
|
||||
|
||||
/*=========================== Forward Declarations ===========================*/
|
||||
struct zpool_handle;
|
||||
typedef struct zpool_handle zpool_handle_t;
|
||||
|
||||
struct nvlist;
|
||||
typedef struct nvlist nvlist_t;
|
||||
|
||||
/*============================= Class Definitions ============================*/
|
||||
/*-------------------------------- DevfsEvent --------------------------------*/
|
||||
class DevfsEvent : public DevdCtl::DevfsEvent
|
||||
{
|
||||
public:
|
||||
/** Specialized DevdCtlEvent object factory for Devfs events. */
|
||||
static BuildMethod Builder;
|
||||
|
||||
virtual DevdCtl::Event *DeepCopy() const;
|
||||
|
||||
/**
|
||||
* Interpret and perform any actions necessary to
|
||||
* consume the event.
|
||||
* \return True if this event should be queued for later reevaluation
|
||||
*/
|
||||
virtual bool Process() const;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* \brief Read and return label information for a device.
|
||||
*
|
||||
* \param devFd The device from which to read ZFS label information.
|
||||
* \param inUse The device is part of an active or potentially
|
||||
* active configuration.
|
||||
* \param degraded The device label indicates the vdev is not healthy.
|
||||
*
|
||||
* \return If label information is available, an nvlist describing
|
||||
* the vdev configuraiton found on the device specified by
|
||||
* devFd. Otherwise NULL.
|
||||
*/
|
||||
static nvlist_t *ReadLabel(int devFd, bool &inUse, bool °raded);
|
||||
|
||||
/**
|
||||
* Attempt to match the ZFS labeled device at devPath with an active
|
||||
* CaseFile for a missing vdev. If a CaseFile is found, attempt
|
||||
* to re-integrate the device with its pool.
|
||||
*
|
||||
* \param devPath The devfs path to the potential leaf vdev.
|
||||
* \param physPath The physical path string reported by the device
|
||||
* at devPath.
|
||||
* \param devConfig The ZFS label information found on the device
|
||||
* at devPath.
|
||||
*
|
||||
* \return true if the event that caused the online action can
|
||||
* be considered consumed.
|
||||
*/
|
||||
static bool OnlineByLabel(const string &devPath,
|
||||
const string& physPath,
|
||||
nvlist_t *devConfig);
|
||||
|
||||
/** DeepCopy Constructor. */
|
||||
DevfsEvent(const DevfsEvent &src);
|
||||
|
||||
/** Constructor */
|
||||
DevfsEvent(Type, DevdCtl::NVPairMap &, const string &);
|
||||
};
|
||||
|
||||
/*--------------------------------- ZfsEvent ---------------------------------*/
|
||||
class ZfsEvent : public DevdCtl::ZfsEvent
|
||||
{
|
||||
public:
|
||||
/** Specialized DevdCtlEvent object factory for ZFS events. */
|
||||
static BuildMethod Builder;
|
||||
|
||||
virtual DevdCtl::Event *DeepCopy() const;
|
||||
|
||||
/**
|
||||
* Interpret and perform any actions necessary to
|
||||
* consume the event.
|
||||
* \return True if this event should be queued for later reevaluation
|
||||
*/
|
||||
virtual bool Process() const;
|
||||
|
||||
protected:
|
||||
/** DeepCopy Constructor. */
|
||||
ZfsEvent(const ZfsEvent &src);
|
||||
|
||||
/** Constructor */
|
||||
ZfsEvent(Type, DevdCtl::NVPairMap &, const string &);
|
||||
|
||||
/**
|
||||
* Detach any spares that are no longer needed, but were not
|
||||
* automatically detached by the kernel
|
||||
*/
|
||||
virtual void CleanupSpares() const;
|
||||
virtual void ProcessPoolEvent() const;
|
||||
static VdevCallback_t TryDetach;
|
||||
};
|
||||
|
||||
class GeomEvent : public DevdCtl::GeomEvent
|
||||
{
|
||||
public:
|
||||
static BuildMethod Builder;
|
||||
|
||||
virtual DevdCtl::Event *DeepCopy() const;
|
||||
|
||||
virtual bool Process() const;
|
||||
|
||||
protected:
|
||||
/** DeepCopy Constructor. */
|
||||
GeomEvent(const GeomEvent &src);
|
||||
|
||||
/** Constructor */
|
||||
GeomEvent(Type, DevdCtl::NVPairMap &, const string &);
|
||||
};
|
||||
#endif /*_ZFSD_EVENT_H_ */
|
134
cddl/usr.sbin/zfsd/zfsd_exception.cc
Normal file
134
cddl/usr.sbin/zfsd/zfsd_exception.cc
Normal file
@ -0,0 +1,134 @@
|
||||
/*-
|
||||
* Copyright (c) 2011, 2012, 2013 Spectra Logic Corporation
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions, and the following disclaimer,
|
||||
* without modification.
|
||||
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
|
||||
* substantially similar to the "NO WARRANTY" disclaimer below
|
||||
* ("Disclaimer") and any redistribution must be conditioned upon
|
||||
* including a substantially similar Disclaimer requirement for further
|
||||
* binary redistribution.
|
||||
*
|
||||
* NO WARRANTY
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGES.
|
||||
*
|
||||
* Authors: Justin T. Gibbs (Spectra Logic Corporation)
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file zfsd_exception
|
||||
*
|
||||
* Implementation of the ZfsdException class.
|
||||
*/
|
||||
#include <sys/cdefs.h>
|
||||
#include <sys/fs/zfs.h>
|
||||
|
||||
#include <syslog.h>
|
||||
|
||||
#include <string>
|
||||
#include <list>
|
||||
#include <sstream>
|
||||
|
||||
#include <devdctl/exception.h>
|
||||
#include <devdctl/guid.h>
|
||||
|
||||
#include <libzfs.h>
|
||||
|
||||
#include "vdev.h"
|
||||
#include "zfsd_exception.h"
|
||||
|
||||
__FBSDID("$FreeBSD$");
|
||||
/*============================ Namespace Control =============================*/
|
||||
using std::endl;
|
||||
using std::string;
|
||||
using std::stringstream;
|
||||
|
||||
/*=========================== Class Implementations ==========================*/
|
||||
/*------------------------------- ZfsdException ------------------------------*/
|
||||
ZfsdException::ZfsdException(const char *fmt, ...)
|
||||
: DevdCtl::Exception(),
|
||||
m_poolConfig(NULL),
|
||||
m_vdevConfig(NULL)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
FormatLog(fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
ZfsdException::ZfsdException(zpool_handle_t *pool, const char *fmt, ...)
|
||||
: DevdCtl::Exception(),
|
||||
m_poolConfig(zpool_get_config(pool, NULL)),
|
||||
m_vdevConfig(NULL)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
FormatLog(fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
ZfsdException::ZfsdException(nvlist_t *poolConfig, const char *fmt, ...)
|
||||
: DevdCtl::Exception(),
|
||||
m_poolConfig(poolConfig),
|
||||
m_vdevConfig(NULL)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
FormatLog(fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void
|
||||
ZfsdException::Log() const
|
||||
{
|
||||
stringstream output;
|
||||
|
||||
if (m_poolConfig != NULL) {
|
||||
|
||||
output << "Pool ";
|
||||
|
||||
char *poolName;
|
||||
if (nvlist_lookup_string(m_poolConfig, ZPOOL_CONFIG_POOL_NAME,
|
||||
&poolName) == 0)
|
||||
output << poolName;
|
||||
else
|
||||
output << "Unknown";
|
||||
output << ": ";
|
||||
}
|
||||
|
||||
if (m_vdevConfig != NULL) {
|
||||
|
||||
if (m_poolConfig != NULL) {
|
||||
Vdev vdev(m_poolConfig, m_vdevConfig);
|
||||
|
||||
output << "Vdev " << vdev.GUID() << ": ";
|
||||
} else {
|
||||
Vdev vdev(m_vdevConfig);
|
||||
|
||||
output << "Pool " << vdev.PoolGUID() << ": ";
|
||||
output << "Vdev " << vdev.GUID() << ": ";
|
||||
}
|
||||
}
|
||||
|
||||
output << m_log << endl;
|
||||
syslog(LOG_ERR, "%s", output.str().c_str());
|
||||
}
|
||||
|
109
cddl/usr.sbin/zfsd/zfsd_exception.h
Normal file
109
cddl/usr.sbin/zfsd/zfsd_exception.h
Normal file
@ -0,0 +1,109 @@
|
||||
/*-
|
||||
* Copyright (c) 2011, 2012, 2013 Spectra Logic Corporation
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions, and the following disclaimer,
|
||||
* without modification.
|
||||
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
|
||||
* substantially similar to the "NO WARRANTY" disclaimer below
|
||||
* ("Disclaimer") and any redistribution must be conditioned upon
|
||||
* including a substantially similar Disclaimer requirement for further
|
||||
* binary redistribution.
|
||||
*
|
||||
* NO WARRANTY
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGES.
|
||||
*
|
||||
* Authors: Justin T. Gibbs (Spectra Logic Corporation)
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file zfsd_exception.h
|
||||
*
|
||||
* Definition of the ZfsdException class hierarchy. All exceptions
|
||||
* explicitly thrown by Zfsd are defined here.
|
||||
*
|
||||
* Header requirements:
|
||||
* #include <string>
|
||||
*
|
||||
* #include <devdctl/exception.h>
|
||||
*/
|
||||
#ifndef _ZFSD_EXCEPTION_H_
|
||||
#define _ZFSD_EXCEPTION_H_
|
||||
|
||||
/*=========================== Forward Declarations ===========================*/
|
||||
struct zpool_handle;
|
||||
typedef struct zpool_handle zpool_handle_t;
|
||||
|
||||
struct nvlist;
|
||||
typedef struct nvlist nvlist_t;
|
||||
|
||||
/*============================= Class Definitions ============================*/
|
||||
/*------------------------------- ZfsdException ------------------------------*/
|
||||
/**
|
||||
* \brief Class allowing unified reporting/logging of exceptional events.
|
||||
*/
|
||||
class ZfsdException : public DevdCtl::Exception
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* \brief ZfsdException constructor allowing arbitrary string
|
||||
* data to be reported.
|
||||
*
|
||||
* \param fmt Printf-like string format specifier.
|
||||
*/
|
||||
ZfsdException(const char *fmt, ...);
|
||||
|
||||
/**
|
||||
* \brief ZfsdException constructor allowing arbitrary string
|
||||
* data to be reported and associated with the configuration
|
||||
* data for a ZFS pool.
|
||||
*
|
||||
* \param pool Pool handle describing the pool to which this
|
||||
* exception is associated.
|
||||
* \param fmt Printf-like string format specifier.
|
||||
*
|
||||
* Instantiation with this method is used to report global
|
||||
* pool errors.
|
||||
*/
|
||||
ZfsdException(zpool_handle_t *pool, const char *, ...);
|
||||
|
||||
/**
|
||||
* \brief ZfsdException constructor allowing arbitrary string
|
||||
* data to be reported and associated with the configuration
|
||||
* data for a ZFS pool.
|
||||
*
|
||||
* \param poolConfig Pool configuration describing the pool to
|
||||
* which this exception is associated.
|
||||
* \param fmt Printf-like string format specifier.
|
||||
*
|
||||
* Instantiation with this method is used to report global
|
||||
* pool errors.
|
||||
*/
|
||||
ZfsdException(nvlist_t *poolConfig, const char *, ...);
|
||||
|
||||
/**
|
||||
* \brief Emit exception data to syslog(3).
|
||||
*/
|
||||
virtual void Log() const;
|
||||
private:
|
||||
nvlist_t *m_poolConfig;
|
||||
nvlist_t *m_vdevConfig;
|
||||
};
|
||||
|
||||
#endif /* _ZFSD_EXCEPTION_H_ */
|
90
cddl/usr.sbin/zfsd/zfsd_main.cc
Normal file
90
cddl/usr.sbin/zfsd/zfsd_main.cc
Normal file
@ -0,0 +1,90 @@
|
||||
/*-
|
||||
* Copyright (c) 2012, 2013 Spectra Logic Corporation
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions, and the following disclaimer,
|
||||
* without modification.
|
||||
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
|
||||
* substantially similar to the "NO WARRANTY" disclaimer below
|
||||
* ("Disclaimer") and any redistribution must be conditioned upon
|
||||
* including a substantially similar Disclaimer requirement for further
|
||||
* binary redistribution.
|
||||
*
|
||||
* NO WARRANTY
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGES.
|
||||
*
|
||||
* Authors: Alan Somers (Spectra Logic Corporation)
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file zfsd_main.cc
|
||||
*
|
||||
* main function for the ZFS Daemon. Separated to facilitate testing.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstdio>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include <devdctl/guid.h>
|
||||
#include <devdctl/event.h>
|
||||
#include <devdctl/event_factory.h>
|
||||
#include <devdctl/exception.h>
|
||||
#include <devdctl/consumer.h>
|
||||
|
||||
#include "vdev_iterator.h"
|
||||
#include "zfsd.h"
|
||||
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
/*=============================== Program Main ===============================*/
|
||||
static void
|
||||
usage()
|
||||
{
|
||||
fprintf(stderr, "usage: %s [-d]\n", getprogname());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Program entry point.
|
||||
*/
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
int ch;
|
||||
|
||||
while ((ch = getopt(argc, argv, "d")) != -1) {
|
||||
switch (ch) {
|
||||
case 'd':
|
||||
g_debug++;
|
||||
break;
|
||||
default:
|
||||
usage();
|
||||
}
|
||||
}
|
||||
|
||||
ZfsDaemon::Run();
|
||||
|
||||
return (0);
|
||||
}
|
121
cddl/usr.sbin/zfsd/zpool_list.cc
Normal file
121
cddl/usr.sbin/zfsd/zpool_list.cc
Normal file
@ -0,0 +1,121 @@
|
||||
/*-
|
||||
* Copyright (c) 2011, 2012, 2013 Spectra Logic Corporation
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions, and the following disclaimer,
|
||||
* without modification.
|
||||
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
|
||||
* substantially similar to the "NO WARRANTY" disclaimer below
|
||||
* ("Disclaimer") and any redistribution must be conditioned upon
|
||||
* including a substantially similar Disclaimer requirement for further
|
||||
* binary redistribution.
|
||||
*
|
||||
* NO WARRANTY
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGES.
|
||||
*
|
||||
* Authors: Justin T. Gibbs (Spectra Logic Corporation)
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file zpool_list.cc
|
||||
*
|
||||
* Implementation of the ZpoolList class.
|
||||
*/
|
||||
#include <sys/cdefs.h>
|
||||
#include <sys/fs/zfs.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <libzfs.h>
|
||||
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include <devdctl/guid.h>
|
||||
#include <devdctl/event.h>
|
||||
#include <devdctl/event_factory.h>
|
||||
#include <devdctl/exception.h>
|
||||
#include <devdctl/consumer.h>
|
||||
|
||||
#include "vdev.h"
|
||||
#include "vdev_iterator.h"
|
||||
#include "zpool_list.h"
|
||||
#include "zfsd.h"
|
||||
|
||||
/*============================ Namespace Control =============================*/
|
||||
using DevdCtl::Guid;
|
||||
|
||||
/*=========================== Class Implementations ==========================*/
|
||||
/*--------------------------------- ZpoolList --------------------------------*/
|
||||
bool
|
||||
ZpoolList::ZpoolAll(zpool_handle_t *pool, nvlist_t *poolConfig, void *cbArg)
|
||||
{
|
||||
return (true);
|
||||
}
|
||||
|
||||
bool
|
||||
ZpoolList::ZpoolByGUID(zpool_handle_t *pool, nvlist_t *poolConfig,
|
||||
void *cbArg)
|
||||
{
|
||||
Guid *desiredPoolGUID(static_cast<Guid *>(cbArg));
|
||||
uint64_t poolGUID;
|
||||
|
||||
/* We are only intested in the pool that matches our pool GUID. */
|
||||
return (nvlist_lookup_uint64(poolConfig, ZPOOL_CONFIG_POOL_GUID,
|
||||
&poolGUID) == 0
|
||||
&& poolGUID == (uint64_t)*desiredPoolGUID);
|
||||
}
|
||||
|
||||
bool
|
||||
ZpoolList::ZpoolByName(zpool_handle_t *pool, nvlist_t *poolConfig, void *cbArg)
|
||||
{
|
||||
const string &desiredPoolName(*static_cast<const string *>(cbArg));
|
||||
|
||||
/* We are only intested in the pool that matches our pool GUID. */
|
||||
return (desiredPoolName == zpool_get_name(pool));
|
||||
}
|
||||
|
||||
int
|
||||
ZpoolList::LoadIterator(zpool_handle_t *pool, void *data)
|
||||
{
|
||||
ZpoolList *zpl(reinterpret_cast<ZpoolList *>(data));
|
||||
nvlist_t *poolConfig(zpool_get_config(pool, NULL));
|
||||
|
||||
if (zpl->m_filter(pool, poolConfig, zpl->m_filterArg))
|
||||
zpl->push_back(pool);
|
||||
else
|
||||
zpool_close(pool);
|
||||
return (0);
|
||||
}
|
||||
|
||||
ZpoolList::ZpoolList(PoolFilter_t *filter, void * filterArg)
|
||||
: m_filter(filter),
|
||||
m_filterArg(filterArg)
|
||||
{
|
||||
zpool_iter(g_zfsHandle, LoadIterator, this);
|
||||
}
|
||||
|
||||
ZpoolList::~ZpoolList()
|
||||
{
|
||||
for (iterator it(begin()); it != end(); it++)
|
||||
zpool_close(*it);
|
||||
|
||||
clear();
|
||||
}
|
133
cddl/usr.sbin/zfsd/zpool_list.h
Normal file
133
cddl/usr.sbin/zfsd/zpool_list.h
Normal file
@ -0,0 +1,133 @@
|
||||
/*-
|
||||
* Copyright (c) 2011, 2012, 2013 Spectra Logic Corporation
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions, and the following disclaimer,
|
||||
* without modification.
|
||||
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
|
||||
* substantially similar to the "NO WARRANTY" disclaimer below
|
||||
* ("Disclaimer") and any redistribution must be conditioned upon
|
||||
* including a substantially similar Disclaimer requirement for further
|
||||
* binary redistribution.
|
||||
*
|
||||
* NO WARRANTY
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGES.
|
||||
*
|
||||
* Authors: Justin T. Gibbs (Spectra Logic Corporation)
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file zpool_list.h
|
||||
*
|
||||
* ZpoolList class definition. ZpoolList is a standard container
|
||||
* allowing filtering and iteration of imported ZFS pool information.
|
||||
*
|
||||
* Header requirements:
|
||||
*
|
||||
* #include <list>
|
||||
* #include <string>
|
||||
*/
|
||||
#ifndef _ZPOOL_LIST_H_
|
||||
#define _ZPOOL_LIST_H_
|
||||
|
||||
/*============================ Namespace Control =============================*/
|
||||
using std::string;
|
||||
|
||||
/*=========================== Forward Declarations ===========================*/
|
||||
struct zpool_handle;
|
||||
typedef struct zpool_handle zpool_handle_t;
|
||||
|
||||
struct nvlist;
|
||||
typedef struct nvlist nvlist_t;
|
||||
|
||||
class Vdev;
|
||||
|
||||
/*============================= Class Definitions ============================*/
|
||||
/*--------------------------------- ZpoolList --------------------------------*/
|
||||
class ZpoolList;
|
||||
typedef bool PoolFilter_t(zpool_handle_t *pool, nvlist_t *poolConfig,
|
||||
void *filterArg);
|
||||
|
||||
/**
|
||||
* \brief Container of imported ZFS pool data.
|
||||
*
|
||||
* ZpoolList is a convenience class that converts libzfs's ZFS
|
||||
* pool methods into a standard list container.
|
||||
*/
|
||||
class ZpoolList : public std::list<zpool_handle_t *>
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* \brief Utility ZpoolList construction filter that causes all
|
||||
* pools known to the system to be included in the
|
||||
* instantiated ZpoolList.
|
||||
*/
|
||||
static PoolFilter_t ZpoolAll;
|
||||
|
||||
/**
|
||||
* \brief Utility ZpoolList construction filter that causes only
|
||||
* a pool known to the system and having the specified GUID
|
||||
* to be included in the instantiated ZpoolList.
|
||||
*/
|
||||
static PoolFilter_t ZpoolByGUID;
|
||||
|
||||
/**
|
||||
* \brief Utility ZpoolList construction filter that causes only
|
||||
* pools known to the system and having the specified name
|
||||
* to be included in the instantiated ZpoolList.
|
||||
*/
|
||||
static PoolFilter_t ZpoolByName;
|
||||
|
||||
/**
|
||||
* \brief ZpoolList contructor
|
||||
*
|
||||
* \param filter The filter function to use when constructing
|
||||
* the ZpoolList. This may be one of the static
|
||||
* utility filters defined for ZpoolList or a
|
||||
* user defined function.
|
||||
* \param filterArg A single argument to pass into the filter function
|
||||
* when it is invoked on each candidate pool.
|
||||
*/
|
||||
ZpoolList(PoolFilter_t *filter = ZpoolAll, void *filterArg = NULL);
|
||||
~ZpoolList();
|
||||
|
||||
private:
|
||||
/**
|
||||
* \brief Helper routine used to populate the internal
|
||||
* data store of ZFS pool objects using libzfs's
|
||||
* zpool_iter() function.
|
||||
*
|
||||
* \param pool The ZFS pool object to filter.
|
||||
* \param data User argument passed through zpool_iter().
|
||||
*/
|
||||
static int LoadIterator(zpool_handle_t *pool, void *data);
|
||||
|
||||
/**
|
||||
* \brief The filter with which this ZpoolList was constructed.
|
||||
*/
|
||||
PoolFilter_t *m_filter;
|
||||
|
||||
/**
|
||||
* \brief The filter argument with which this ZpoolList was
|
||||
* constructed.
|
||||
*/
|
||||
void *m_filterArg;
|
||||
};
|
||||
|
||||
#endif /* _ZPOOL_ITERATOR_H_ */
|
@ -60,6 +60,10 @@ rc_conf_files="/etc/rc.conf /etc/rc.conf.local"
|
||||
# ZFS support
|
||||
zfs_enable="NO" # Set to YES to automatically mount ZFS file systems
|
||||
|
||||
# ZFSD support
|
||||
zfsd_enable="NO" # Set to YES to automatically start the ZFS fault
|
||||
# management daemon.
|
||||
|
||||
gptboot_enable="YES" # GPT boot success/failure reporting.
|
||||
|
||||
# Experimental - test before enabling
|
||||
|
@ -155,6 +155,8 @@
|
||||
wi
|
||||
..
|
||||
..
|
||||
devdctl
|
||||
..
|
||||
edit
|
||||
readline
|
||||
..
|
||||
|
@ -48,8 +48,6 @@
|
||||
cddl
|
||||
lib
|
||||
..
|
||||
sbin
|
||||
..
|
||||
usr.bin
|
||||
..
|
||||
usr.sbin
|
||||
@ -215,6 +213,8 @@
|
||||
..
|
||||
..
|
||||
..
|
||||
zfsd
|
||||
..
|
||||
..
|
||||
..
|
||||
etc
|
||||
@ -308,6 +308,8 @@
|
||||
..
|
||||
libcrypt
|
||||
..
|
||||
libdevdctl
|
||||
..
|
||||
libmp
|
||||
..
|
||||
libnv
|
||||
|
@ -54,6 +54,10 @@
|
||||
..
|
||||
portsnap
|
||||
..
|
||||
zfsd
|
||||
cases
|
||||
..
|
||||
..
|
||||
..
|
||||
empty mode=0555 flags=schg
|
||||
..
|
||||
|
@ -123,7 +123,7 @@ FILES= DAEMON \
|
||||
ypserv \
|
||||
ypset \
|
||||
ypupdated \
|
||||
ypxfrd \
|
||||
ypxfrd
|
||||
|
||||
.if ${MK_ACCT} != "no"
|
||||
FILESGROUPS+= ACCT
|
||||
@ -308,6 +308,7 @@ FILES+= wpa_supplicant
|
||||
.if ${MK_ZFS} != "no"
|
||||
FILESGROUPS+= ZFS
|
||||
ZFS+= zfs
|
||||
ZFS+= zfsd
|
||||
ZFS+= zvol
|
||||
ZFSPACKAGE= zfs
|
||||
.endif
|
||||
|
17
etc/rc.d/zfsd
Normal file
17
etc/rc.d/zfsd
Normal file
@ -0,0 +1,17 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# $FreeBSD$
|
||||
#
|
||||
|
||||
# PROVIDE: zfsd
|
||||
# REQUIRE: devd zfs
|
||||
# KEYWORD: nojail shutdown
|
||||
|
||||
. /etc/rc.subr
|
||||
|
||||
name="zfsd"
|
||||
rcvar="zfsd_enable"
|
||||
command="/usr/sbin/${name}"
|
||||
|
||||
load_rc_config $name
|
||||
run_rc_command "$1"
|
@ -41,6 +41,7 @@ SUBDIR= ${SUBDIR_ORDERED} \
|
||||
libcompat \
|
||||
libcrypt \
|
||||
libdevctl \
|
||||
libdevdctl \
|
||||
libdevinfo \
|
||||
libdevstat \
|
||||
libdpv \
|
||||
|
21
lib/libdevdctl/Makefile
Normal file
21
lib/libdevdctl/Makefile
Normal file
@ -0,0 +1,21 @@
|
||||
# $FreeBSD$
|
||||
|
||||
LIB_CXX= devdctl
|
||||
INCS= consumer.h \
|
||||
event.h \
|
||||
event_factory.h \
|
||||
exception.h \
|
||||
guid.h
|
||||
SRCS= consumer.cc \
|
||||
event.cc \
|
||||
event_factory.cc \
|
||||
exception.cc \
|
||||
guid.cc
|
||||
|
||||
INCSDIR= ${INCLUDEDIR}/devdctl
|
||||
|
||||
WARNS?= 3
|
||||
PRIVATELIB= true
|
||||
SHLIB_MAJOR= 0
|
||||
|
||||
.include <bsd.lib.mk>
|
258
lib/libdevdctl/consumer.cc
Normal file
258
lib/libdevdctl/consumer.cc
Normal file
@ -0,0 +1,258 @@
|
||||
/*-
|
||||
* Copyright (c) 2011, 2012, 2013, 2014 Spectra Logic Corporation
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions, and the following disclaimer,
|
||||
* without modification.
|
||||
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
|
||||
* substantially similar to the "NO WARRANTY" disclaimer below
|
||||
* ("Disclaimer") and any redistribution must be conditioned upon
|
||||
* including a substantially similar Disclaimer requirement for further
|
||||
* binary redistribution.
|
||||
*
|
||||
* NO WARRANTY
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGES.
|
||||
*
|
||||
* Authors: Justin T. Gibbs (Spectra Logic Corporation)
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file consumer.cc
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
#include <sys/poll.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <syslog.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <cstdarg>
|
||||
#include <cstring>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include "guid.h"
|
||||
#include "event.h"
|
||||
#include "event_factory.h"
|
||||
#include "exception.h"
|
||||
|
||||
#include "consumer.h"
|
||||
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
/*================================== Macros ==================================*/
|
||||
#define NUM_ELEMENTS(x) (sizeof(x) / sizeof(*x))
|
||||
|
||||
/*============================ Namespace Control =============================*/
|
||||
using std::string;
|
||||
namespace DevdCtl
|
||||
{
|
||||
|
||||
/*============================= Class Definitions ============================*/
|
||||
/*----------------------------- DevdCtl::Consumer ----------------------------*/
|
||||
//- Consumer Static Private Data -----------------------------------------------
|
||||
const char Consumer::s_devdSockPath[] = "/var/run/devd.seqpacket.pipe";
|
||||
|
||||
//- Consumer Public Methods ----------------------------------------------------
|
||||
Consumer::Consumer(Event::BuildMethod *defBuilder,
|
||||
EventFactory::Record *regEntries,
|
||||
size_t numEntries)
|
||||
: m_devdSockFD(-1),
|
||||
m_eventFactory(defBuilder),
|
||||
m_replayingEvents(false)
|
||||
{
|
||||
m_eventFactory.UpdateRegistry(regEntries, numEntries);
|
||||
}
|
||||
|
||||
Consumer::~Consumer()
|
||||
{
|
||||
DisconnectFromDevd();
|
||||
}
|
||||
|
||||
bool
|
||||
Consumer::ConnectToDevd()
|
||||
{
|
||||
struct sockaddr_un devdAddr;
|
||||
int sLen;
|
||||
int result;
|
||||
|
||||
if (m_devdSockFD != -1) {
|
||||
/* Already connected. */
|
||||
syslog(LOG_DEBUG, "%s: Already connected.", __func__);
|
||||
return (true);
|
||||
}
|
||||
syslog(LOG_INFO, "%s: Connecting to devd.", __func__);
|
||||
|
||||
memset(&devdAddr, 0, sizeof(devdAddr));
|
||||
devdAddr.sun_family= AF_UNIX;
|
||||
strlcpy(devdAddr.sun_path, s_devdSockPath, sizeof(devdAddr.sun_path));
|
||||
sLen = SUN_LEN(&devdAddr);
|
||||
|
||||
m_devdSockFD = socket(AF_UNIX, SOCK_SEQPACKET, 0);
|
||||
if (m_devdSockFD == -1)
|
||||
err(1, "Unable to create socket");
|
||||
if (fcntl(m_devdSockFD, F_SETFL, O_NONBLOCK) < 0)
|
||||
err(1, "fcntl");
|
||||
result = connect(m_devdSockFD,
|
||||
reinterpret_cast<sockaddr *>(&devdAddr),
|
||||
sLen);
|
||||
if (result == -1) {
|
||||
syslog(LOG_INFO, "Unable to connect to devd");
|
||||
DisconnectFromDevd();
|
||||
return (false);
|
||||
}
|
||||
|
||||
syslog(LOG_INFO, "Connection to devd successful");
|
||||
return (true);
|
||||
}
|
||||
|
||||
void
|
||||
Consumer::DisconnectFromDevd()
|
||||
{
|
||||
if (m_devdSockFD != -1)
|
||||
syslog(LOG_INFO, "Disconnecting from devd.");
|
||||
|
||||
close(m_devdSockFD);
|
||||
m_devdSockFD = -1;
|
||||
}
|
||||
|
||||
std::string
|
||||
Consumer::ReadEvent()
|
||||
{
|
||||
char buf[MAX_EVENT_SIZE + 1];
|
||||
ssize_t len;
|
||||
|
||||
len = ::recv(m_devdSockFD, buf, MAX_EVENT_SIZE, MSG_WAITALL);
|
||||
if (len == -1)
|
||||
return (std::string(""));
|
||||
else {
|
||||
/* NULL-terminate the result */
|
||||
buf[len] = '\0';
|
||||
return (std::string(buf));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Consumer::ReplayUnconsumedEvents(bool discardUnconsumed)
|
||||
{
|
||||
EventList::iterator event(m_unconsumedEvents.begin());
|
||||
bool replayed_any = (event != m_unconsumedEvents.end());
|
||||
|
||||
m_replayingEvents = true;
|
||||
if (replayed_any)
|
||||
syslog(LOG_INFO, "Started replaying unconsumed events");
|
||||
while (event != m_unconsumedEvents.end()) {
|
||||
bool consumed((*event)->Process());
|
||||
if (consumed || discardUnconsumed) {
|
||||
delete *event;
|
||||
event = m_unconsumedEvents.erase(event);
|
||||
} else {
|
||||
event++;
|
||||
}
|
||||
}
|
||||
if (replayed_any)
|
||||
syslog(LOG_INFO, "Finished replaying unconsumed events");
|
||||
m_replayingEvents = false;
|
||||
}
|
||||
|
||||
bool
|
||||
Consumer::SaveEvent(const Event &event)
|
||||
{
|
||||
if (m_replayingEvents)
|
||||
return (false);
|
||||
m_unconsumedEvents.push_back(event.DeepCopy());
|
||||
return (true);
|
||||
}
|
||||
|
||||
Event *
|
||||
Consumer::NextEvent()
|
||||
{
|
||||
if (!Connected())
|
||||
return(NULL);
|
||||
|
||||
Event *event(NULL);
|
||||
try {
|
||||
string evString;
|
||||
|
||||
evString = ReadEvent();
|
||||
if (! evString.empty()) {
|
||||
Event::TimestampEventString(evString);
|
||||
event = Event::CreateEvent(m_eventFactory, evString);
|
||||
}
|
||||
} catch (const Exception &exp) {
|
||||
exp.Log();
|
||||
DisconnectFromDevd();
|
||||
}
|
||||
return (event);
|
||||
}
|
||||
|
||||
/* Capture and process buffered events. */
|
||||
void
|
||||
Consumer::ProcessEvents()
|
||||
{
|
||||
Event *event;
|
||||
while ((event = NextEvent()) != NULL) {
|
||||
if (event->Process())
|
||||
SaveEvent(*event);
|
||||
delete event;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Consumer::FlushEvents()
|
||||
{
|
||||
std::string s;
|
||||
|
||||
do
|
||||
s = ReadEvent();
|
||||
while (! s.empty()) ;
|
||||
}
|
||||
|
||||
bool
|
||||
Consumer::EventsPending()
|
||||
{
|
||||
struct pollfd fds[1];
|
||||
int result;
|
||||
|
||||
do {
|
||||
fds->fd = m_devdSockFD;
|
||||
fds->events = POLLIN;
|
||||
fds->revents = 0;
|
||||
result = poll(fds, NUM_ELEMENTS(fds), /*timeout*/0);
|
||||
} while (result == -1 && errno == EINTR);
|
||||
|
||||
if (result == -1)
|
||||
err(1, "Polling for devd events failed");
|
||||
|
||||
if ((fds->revents & POLLERR) != 0)
|
||||
throw Exception("Consumer::EventsPending(): "
|
||||
"POLLERR detected on devd socket.");
|
||||
|
||||
if ((fds->revents & POLLHUP) != 0)
|
||||
throw Exception("Consumer::EventsPending(): "
|
||||
"POLLHUP detected on devd socket.");
|
||||
|
||||
return ((fds->revents & POLLIN) != 0);
|
||||
}
|
||||
|
||||
} // namespace DevdCtl
|
186
lib/libdevdctl/consumer.h
Normal file
186
lib/libdevdctl/consumer.h
Normal file
@ -0,0 +1,186 @@
|
||||
/*-
|
||||
* Copyright (c) 2011, 2012, 2013, 2014 Spectra Logic Corporation
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions, and the following disclaimer,
|
||||
* without modification.
|
||||
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
|
||||
* substantially similar to the "NO WARRANTY" disclaimer below
|
||||
* ("Disclaimer") and any redistribution must be conditioned upon
|
||||
* including a substantially similar Disclaimer requirement for further
|
||||
* binary redistribution.
|
||||
*
|
||||
* NO WARRANTY
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGES.
|
||||
*
|
||||
* Authors: Justin T. Gibbs (Spectra Logic Corporation)
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file devdctl_consumer.h
|
||||
*/
|
||||
#ifndef _DEVDCTL_CONSUMER_H_
|
||||
#define _DEVDCTL_CONSUMER_H_
|
||||
|
||||
/*============================ Namespace Control =============================*/
|
||||
namespace DevdCtl
|
||||
{
|
||||
|
||||
/*=========================== Forward Declarations ===========================*/
|
||||
class Event;
|
||||
class EventBuffer;
|
||||
class FDReader;
|
||||
|
||||
/*============================ Class Declarations ============================*/
|
||||
/*----------------------------- DevdCtl::Consumer ----------------------------*/
|
||||
|
||||
/**
|
||||
*/
|
||||
class Consumer
|
||||
{
|
||||
public:
|
||||
Consumer(Event::BuildMethod *defBuilder = NULL,
|
||||
EventFactory::Record *regEntries = NULL,
|
||||
size_t numEntries = 0);
|
||||
virtual ~Consumer();
|
||||
|
||||
bool Connected() const;
|
||||
|
||||
/**
|
||||
* Return file descriptor useful for client's wishing to poll(2)
|
||||
* for new events.
|
||||
*/
|
||||
int GetPollFd();
|
||||
|
||||
/**
|
||||
* Queue an event for deferred processing or replay.
|
||||
*/
|
||||
bool SaveEvent(const Event &event);
|
||||
|
||||
/**
|
||||
* Reprocess any events saved via the SaveEvent() facility.
|
||||
*
|
||||
* \param discardUnconsumed If true, events that are not consumed
|
||||
* during replay are discarded.
|
||||
*/
|
||||
void ReplayUnconsumedEvents(bool discardUnconsumed);
|
||||
|
||||
/** Return an event, if one is available. */
|
||||
Event *NextEvent();
|
||||
|
||||
/**
|
||||
* Extract events and invoke each event's Process method.
|
||||
*/
|
||||
void ProcessEvents();
|
||||
|
||||
/** Discard all data pending in m_devdSockFD. */
|
||||
void FlushEvents();
|
||||
|
||||
/**
|
||||
* Test for data pending in m_devdSockFD
|
||||
*
|
||||
* \return True if data is pending. Otherwise false.
|
||||
*/
|
||||
bool EventsPending();
|
||||
|
||||
/**
|
||||
* Open a connection to devd's unix domain socket.
|
||||
*
|
||||
* \return True if the connection attempt is successsful. Otherwise
|
||||
* false.
|
||||
*/
|
||||
bool ConnectToDevd();
|
||||
|
||||
/**
|
||||
* Close a connection (if any) to devd's unix domain socket.
|
||||
*/
|
||||
void DisconnectFromDevd();
|
||||
|
||||
EventFactory GetFactory();
|
||||
|
||||
protected:
|
||||
/**
|
||||
* \brief Reads the most recent record
|
||||
*
|
||||
* On error, "" is returned, and errno will be set by the OS
|
||||
*
|
||||
* \returns A string containing the record
|
||||
*/
|
||||
std::string ReadEvent();
|
||||
|
||||
enum {
|
||||
/*
|
||||
* The maximum event size supported by libdevdctl.
|
||||
*/
|
||||
MAX_EVENT_SIZE = 8192,
|
||||
};
|
||||
|
||||
static const char s_devdSockPath[];
|
||||
|
||||
/**
|
||||
* File descriptor representing the unix domain socket
|
||||
* connection with devd.
|
||||
*/
|
||||
int m_devdSockFD;
|
||||
|
||||
/**
|
||||
* Reader tied to the devd socket.
|
||||
*/
|
||||
FDReader *m_reader;
|
||||
|
||||
/**
|
||||
* Default EventBuffer connected to m_reader.
|
||||
*/
|
||||
EventBuffer *m_eventBuffer;
|
||||
|
||||
EventFactory m_eventFactory;
|
||||
|
||||
/** Queued events for replay. */
|
||||
EventList m_unconsumedEvents;
|
||||
|
||||
/**
|
||||
* Flag controlling whether events can be queued. This boolean
|
||||
* is set during event replay to ensure that previosuly deferred
|
||||
* events are not requeued and thus retained forever.
|
||||
*/
|
||||
bool m_replayingEvents;
|
||||
};
|
||||
|
||||
//- Consumer Const Public Inline Methods ---------------------------------------
|
||||
inline bool
|
||||
Consumer::Connected() const
|
||||
{
|
||||
return (m_devdSockFD != -1);
|
||||
}
|
||||
|
||||
//- Consumer Public Inline Methods ---------------------------------------------
|
||||
inline int
|
||||
Consumer::GetPollFd()
|
||||
{
|
||||
return (m_devdSockFD);
|
||||
}
|
||||
|
||||
inline EventFactory
|
||||
Consumer::GetFactory()
|
||||
{
|
||||
return (m_eventFactory);
|
||||
}
|
||||
|
||||
} // namespace DevdCtl
|
||||
#endif /* _DEVDCTL_CONSUMER_H_ */
|
602
lib/libdevdctl/event.cc
Normal file
602
lib/libdevdctl/event.cc
Normal file
@ -0,0 +1,602 @@
|
||||
/*-
|
||||
* Copyright (c) 2011, 2012, 2013, 2016 Spectra Logic Corporation
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions, and the following disclaimer,
|
||||
* without modification.
|
||||
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
|
||||
* substantially similar to the "NO WARRANTY" disclaimer below
|
||||
* ("Disclaimer") and any redistribution must be conditioned upon
|
||||
* including a substantially similar Disclaimer requirement for further
|
||||
* binary redistribution.
|
||||
*
|
||||
* NO WARRANTY
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGES.
|
||||
*
|
||||
* Authors: Justin T. Gibbs (Spectra Logic Corporation)
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file event.cc
|
||||
*
|
||||
* Implementation of the class hierarchy used to express events
|
||||
* received via the devdctl API.
|
||||
*/
|
||||
#include <sys/cdefs.h>
|
||||
#include <sys/disk.h>
|
||||
#include <sys/filio.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <err.h>
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
#include <paths.h>
|
||||
#include <stdlib.h>
|
||||
#include <syslog.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <cstdarg>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include "guid.h"
|
||||
#include "event.h"
|
||||
#include "event_factory.h"
|
||||
#include "exception.h"
|
||||
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
/*================================== Macros ==================================*/
|
||||
#define NUM_ELEMENTS(x) (sizeof(x) / sizeof(*x))
|
||||
|
||||
/*============================ Namespace Control =============================*/
|
||||
using std::cout;
|
||||
using std::endl;
|
||||
using std::string;
|
||||
using std::stringstream;
|
||||
|
||||
namespace DevdCtl
|
||||
{
|
||||
|
||||
/*=========================== Class Implementations ==========================*/
|
||||
/*----------------------------------- Event ----------------------------------*/
|
||||
//- Event Static Protected Data ------------------------------------------------
|
||||
const string Event::s_theEmptyString;
|
||||
|
||||
Event::EventTypeRecord Event::s_typeTable[] =
|
||||
{
|
||||
{ Event::NOTIFY, "Notify" },
|
||||
{ Event::NOMATCH, "No Driver Match" },
|
||||
{ Event::ATTACH, "Attach" },
|
||||
{ Event::DETACH, "Detach" }
|
||||
};
|
||||
|
||||
//- Event Static Public Methods ------------------------------------------------
|
||||
Event *
|
||||
Event::Builder(Event::Type type, NVPairMap &nvPairs,
|
||||
const string &eventString)
|
||||
{
|
||||
return (new Event(type, nvPairs, eventString));
|
||||
}
|
||||
|
||||
Event *
|
||||
Event::CreateEvent(const EventFactory &factory, const string &eventString)
|
||||
{
|
||||
NVPairMap &nvpairs(*new NVPairMap);
|
||||
Type type(static_cast<Event::Type>(eventString[0]));
|
||||
|
||||
try {
|
||||
ParseEventString(type, eventString, nvpairs);
|
||||
} catch (const ParseException &exp) {
|
||||
if (exp.GetType() == ParseException::INVALID_FORMAT)
|
||||
exp.Log();
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Allow entries in our table for events with no system specified.
|
||||
* These entries should specify the string "none".
|
||||
*/
|
||||
NVPairMap::iterator system_item(nvpairs.find("system"));
|
||||
if (system_item == nvpairs.end())
|
||||
nvpairs["system"] = "none";
|
||||
|
||||
return (factory.Build(type, nvpairs, eventString));
|
||||
}
|
||||
|
||||
bool
|
||||
Event::DevName(std::string &name) const
|
||||
{
|
||||
return (false);
|
||||
}
|
||||
|
||||
/* TODO: simplify this function with C++-11 <regex> methods */
|
||||
bool
|
||||
Event::IsDiskDev() const
|
||||
{
|
||||
const int numDrivers = 2;
|
||||
static const char *diskDevNames[numDrivers] =
|
||||
{
|
||||
"da",
|
||||
"ada"
|
||||
};
|
||||
const char **dName;
|
||||
string devName;
|
||||
|
||||
if (! DevName(devName))
|
||||
return false;
|
||||
|
||||
size_t find_start = devName.rfind('/');
|
||||
if (find_start == string::npos) {
|
||||
find_start = 0;
|
||||
} else {
|
||||
/* Just after the last '/'. */
|
||||
find_start++;
|
||||
}
|
||||
|
||||
for (dName = &diskDevNames[0];
|
||||
dName <= &diskDevNames[numDrivers - 1]; dName++) {
|
||||
|
||||
size_t loc(devName.find(*dName, find_start));
|
||||
if (loc == find_start) {
|
||||
size_t prefixLen(strlen(*dName));
|
||||
|
||||
if (devName.length() - find_start >= prefixLen
|
||||
&& isdigit(devName[find_start + prefixLen]))
|
||||
return (true);
|
||||
}
|
||||
}
|
||||
|
||||
return (false);
|
||||
}
|
||||
|
||||
const char *
|
||||
Event::TypeToString(Event::Type type)
|
||||
{
|
||||
EventTypeRecord *rec(s_typeTable);
|
||||
EventTypeRecord *lastRec(s_typeTable + NUM_ELEMENTS(s_typeTable) - 1);
|
||||
|
||||
for (; rec <= lastRec; rec++) {
|
||||
if (rec->m_type == type)
|
||||
return (rec->m_typeName);
|
||||
}
|
||||
return ("Unknown");
|
||||
}
|
||||
|
||||
//- Event Public Methods -------------------------------------------------------
|
||||
const string &
|
||||
Event::Value(const string &varName) const
|
||||
{
|
||||
NVPairMap::const_iterator item(m_nvPairs.find(varName));
|
||||
if (item == m_nvPairs.end())
|
||||
return (s_theEmptyString);
|
||||
|
||||
return (item->second);
|
||||
}
|
||||
|
||||
bool
|
||||
Event::Contains(const string &varName) const
|
||||
{
|
||||
return (m_nvPairs.find(varName) != m_nvPairs.end());
|
||||
}
|
||||
|
||||
string
|
||||
Event::ToString() const
|
||||
{
|
||||
stringstream result;
|
||||
|
||||
NVPairMap::const_iterator devName(m_nvPairs.find("device-name"));
|
||||
if (devName != m_nvPairs.end())
|
||||
result << devName->second << ": ";
|
||||
|
||||
NVPairMap::const_iterator systemName(m_nvPairs.find("system"));
|
||||
if (systemName != m_nvPairs.end()
|
||||
&& systemName->second != "none")
|
||||
result << systemName->second << ": ";
|
||||
|
||||
result << TypeToString(GetType()) << ' ';
|
||||
|
||||
for (NVPairMap::const_iterator curVar = m_nvPairs.begin();
|
||||
curVar != m_nvPairs.end(); curVar++) {
|
||||
if (curVar == devName || curVar == systemName)
|
||||
continue;
|
||||
|
||||
result << ' '
|
||||
<< curVar->first << "=" << curVar->second;
|
||||
}
|
||||
result << endl;
|
||||
|
||||
return (result.str());
|
||||
}
|
||||
|
||||
void
|
||||
Event::Print() const
|
||||
{
|
||||
cout << ToString() << std::flush;
|
||||
}
|
||||
|
||||
void
|
||||
Event::Log(int priority) const
|
||||
{
|
||||
syslog(priority, "%s", ToString().c_str());
|
||||
}
|
||||
|
||||
//- Event Virtual Public Methods -----------------------------------------------
|
||||
Event::~Event()
|
||||
{
|
||||
delete &m_nvPairs;
|
||||
}
|
||||
|
||||
Event *
|
||||
Event::DeepCopy() const
|
||||
{
|
||||
return (new Event(*this));
|
||||
}
|
||||
|
||||
bool
|
||||
Event::Process() const
|
||||
{
|
||||
return (false);
|
||||
}
|
||||
|
||||
timeval
|
||||
Event::GetTimestamp() const
|
||||
{
|
||||
timeval tv_timestamp;
|
||||
struct tm tm_timestamp;
|
||||
|
||||
if (!Contains("timestamp")) {
|
||||
throw Exception("Event contains no timestamp: %s",
|
||||
m_eventString.c_str());
|
||||
}
|
||||
strptime(Value(string("timestamp")).c_str(), "%s", &tm_timestamp);
|
||||
tv_timestamp.tv_sec = mktime(&tm_timestamp);
|
||||
tv_timestamp.tv_usec = 0;
|
||||
return (tv_timestamp);
|
||||
}
|
||||
|
||||
bool
|
||||
Event::DevPath(std::string &path) const
|
||||
{
|
||||
string devName;
|
||||
|
||||
if (!DevName(devName))
|
||||
return (false);
|
||||
|
||||
string devPath(_PATH_DEV + devName);
|
||||
int devFd(open(devPath.c_str(), O_RDONLY));
|
||||
if (devFd == -1)
|
||||
return (false);
|
||||
|
||||
/* Normalize the device name in case the DEVFS event is for a link. */
|
||||
devName = fdevname(devFd);
|
||||
path = _PATH_DEV + devName;
|
||||
|
||||
close(devFd);
|
||||
|
||||
return (true);
|
||||
}
|
||||
|
||||
bool
|
||||
Event::PhysicalPath(std::string &path) const
|
||||
{
|
||||
string devPath;
|
||||
|
||||
if (!DevPath(devPath))
|
||||
return (false);
|
||||
|
||||
int devFd(open(devPath.c_str(), O_RDONLY));
|
||||
if (devFd == -1)
|
||||
return (false);
|
||||
|
||||
char physPath[MAXPATHLEN];
|
||||
physPath[0] = '\0';
|
||||
bool result(ioctl(devFd, DIOCGPHYSPATH, physPath) == 0);
|
||||
close(devFd);
|
||||
if (result)
|
||||
path = physPath;
|
||||
return (result);
|
||||
}
|
||||
|
||||
//- Event Protected Methods ----------------------------------------------------
|
||||
Event::Event(Type type, NVPairMap &map, const string &eventString)
|
||||
: m_type(type),
|
||||
m_nvPairs(map),
|
||||
m_eventString(eventString)
|
||||
{
|
||||
}
|
||||
|
||||
Event::Event(const Event &src)
|
||||
: m_type(src.m_type),
|
||||
m_nvPairs(*new NVPairMap(src.m_nvPairs)),
|
||||
m_eventString(src.m_eventString)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
Event::ParseEventString(Event::Type type,
|
||||
const string &eventString,
|
||||
NVPairMap& nvpairs)
|
||||
{
|
||||
size_t start;
|
||||
size_t end;
|
||||
|
||||
switch (type) {
|
||||
case ATTACH:
|
||||
case DETACH:
|
||||
|
||||
/*
|
||||
* <type><device-name><unit> <pnpvars> \
|
||||
* at <location vars> <pnpvars> \
|
||||
* on <parent>
|
||||
*
|
||||
* Handle all data that doesn't conform to the
|
||||
* "name=value" format, and let the generic parser
|
||||
* below handle the rest.
|
||||
*
|
||||
* Type is a single char. Skip it.
|
||||
*/
|
||||
start = 1;
|
||||
end = eventString.find_first_of(" \t\n", start);
|
||||
if (end == string::npos)
|
||||
throw ParseException(ParseException::INVALID_FORMAT,
|
||||
eventString, start);
|
||||
|
||||
nvpairs["device-name"] = eventString.substr(start, end - start);
|
||||
|
||||
start = eventString.find(" on ", end);
|
||||
if (end == string::npos)
|
||||
throw ParseException(ParseException::INVALID_FORMAT,
|
||||
eventString, start);
|
||||
start += 4;
|
||||
end = eventString.find_first_of(" \t\n", start);
|
||||
nvpairs["parent"] = eventString.substr(start, end);
|
||||
break;
|
||||
case NOTIFY:
|
||||
break;
|
||||
case NOMATCH:
|
||||
throw ParseException(ParseException::DISCARDED_EVENT_TYPE,
|
||||
eventString);
|
||||
default:
|
||||
throw ParseException(ParseException::UNKNOWN_EVENT_TYPE,
|
||||
eventString);
|
||||
}
|
||||
|
||||
/* Process common "key=value" format. */
|
||||
for (start = 1; start < eventString.length(); start = end + 1) {
|
||||
|
||||
/* Find the '=' in the middle of the key/value pair. */
|
||||
end = eventString.find('=', start);
|
||||
if (end == string::npos)
|
||||
break;
|
||||
|
||||
/*
|
||||
* Find the start of the key by backing up until
|
||||
* we hit whitespace or '!' (event type "notice").
|
||||
* Due to the devdctl format, all key/value pair must
|
||||
* start with one of these two characters.
|
||||
*/
|
||||
start = eventString.find_last_of("! \t\n", end);
|
||||
if (start == string::npos)
|
||||
throw ParseException(ParseException::INVALID_FORMAT,
|
||||
eventString, end);
|
||||
start++;
|
||||
string key(eventString.substr(start, end - start));
|
||||
|
||||
/*
|
||||
* Walk forward from the '=' until either we exhaust
|
||||
* the buffer or we hit whitespace.
|
||||
*/
|
||||
start = end + 1;
|
||||
if (start >= eventString.length())
|
||||
throw ParseException(ParseException::INVALID_FORMAT,
|
||||
eventString, end);
|
||||
end = eventString.find_first_of(" \t\n", start);
|
||||
if (end == string::npos)
|
||||
end = eventString.length() - 1;
|
||||
string value(eventString.substr(start, end - start));
|
||||
|
||||
nvpairs[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Event::TimestampEventString(std::string &eventString)
|
||||
{
|
||||
if (eventString.size() > 0) {
|
||||
/*
|
||||
* Add a timestamp as the final field of the event if it is
|
||||
* not already present.
|
||||
*/
|
||||
if (eventString.find("timestamp=") == string::npos) {
|
||||
const size_t bufsize = 32; // Long enough for a 64-bit int
|
||||
timeval now;
|
||||
char timebuf[bufsize];
|
||||
|
||||
size_t eventEnd(eventString.find_last_not_of('\n') + 1);
|
||||
if (gettimeofday(&now, NULL) != 0)
|
||||
err(1, "gettimeofday");
|
||||
snprintf(timebuf, bufsize, " timestamp=%"PRId64,
|
||||
(int64_t) now.tv_sec);
|
||||
eventString.insert(eventEnd, timebuf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*-------------------------------- DevfsEvent --------------------------------*/
|
||||
//- DevfsEvent Static Public Methods -------------------------------------------
|
||||
Event *
|
||||
DevfsEvent::Builder(Event::Type type, NVPairMap &nvPairs,
|
||||
const string &eventString)
|
||||
{
|
||||
return (new DevfsEvent(type, nvPairs, eventString));
|
||||
}
|
||||
|
||||
//- DevfsEvent Static Protected Methods ----------------------------------------
|
||||
bool
|
||||
DevfsEvent::IsWholeDev(const string &devName)
|
||||
{
|
||||
string::const_iterator i(devName.begin());
|
||||
|
||||
size_t start = devName.rfind('/');
|
||||
if (start == string::npos) {
|
||||
start = 0;
|
||||
} else {
|
||||
/* Just after the last '/'. */
|
||||
start++;
|
||||
}
|
||||
i += start;
|
||||
|
||||
/* alpha prefix followed only by digits. */
|
||||
for (; i < devName.end() && !isdigit(*i); i++)
|
||||
;
|
||||
|
||||
if (i == devName.end())
|
||||
return (false);
|
||||
|
||||
for (; i < devName.end() && isdigit(*i); i++)
|
||||
;
|
||||
|
||||
return (i == devName.end());
|
||||
}
|
||||
|
||||
//- DevfsEvent Virtual Public Methods ------------------------------------------
|
||||
Event *
|
||||
DevfsEvent::DeepCopy() const
|
||||
{
|
||||
return (new DevfsEvent(*this));
|
||||
}
|
||||
|
||||
bool
|
||||
DevfsEvent::Process() const
|
||||
{
|
||||
return (true);
|
||||
}
|
||||
|
||||
//- DevfsEvent Public Methods --------------------------------------------------
|
||||
bool
|
||||
DevfsEvent::IsWholeDev() const
|
||||
{
|
||||
string devName;
|
||||
|
||||
return (DevName(devName) && IsDiskDev() && IsWholeDev(devName));
|
||||
}
|
||||
|
||||
bool
|
||||
DevfsEvent::DevName(std::string &name) const
|
||||
{
|
||||
if (Value("subsystem") != "CDEV")
|
||||
return (false);
|
||||
|
||||
name = Value("cdev");
|
||||
return (!name.empty());
|
||||
}
|
||||
|
||||
//- DevfsEvent Protected Methods -----------------------------------------------
|
||||
DevfsEvent::DevfsEvent(Event::Type type, NVPairMap &nvpairs,
|
||||
const string &eventString)
|
||||
: Event(type, nvpairs, eventString)
|
||||
{
|
||||
}
|
||||
|
||||
DevfsEvent::DevfsEvent(const DevfsEvent &src)
|
||||
: Event(src)
|
||||
{
|
||||
}
|
||||
|
||||
/*--------------------------------- GeomEvent --------------------------------*/
|
||||
//- GeomEvent Static Public Methods --------------------------------------------
|
||||
Event *
|
||||
GeomEvent::Builder(Event::Type type, NVPairMap &nvpairs,
|
||||
const string &eventString)
|
||||
{
|
||||
return (new GeomEvent(type, nvpairs, eventString));
|
||||
}
|
||||
|
||||
//- GeomEvent Virtual Public Methods -------------------------------------------
|
||||
Event *
|
||||
GeomEvent::DeepCopy() const
|
||||
{
|
||||
return (new GeomEvent(*this));
|
||||
}
|
||||
|
||||
bool
|
||||
GeomEvent::DevName(std::string &name) const
|
||||
{
|
||||
name = Value("devname");
|
||||
return (!name.empty());
|
||||
}
|
||||
|
||||
|
||||
//- GeomEvent Protected Methods ------------------------------------------------
|
||||
GeomEvent::GeomEvent(Event::Type type, NVPairMap &nvpairs,
|
||||
const string &eventString)
|
||||
: Event(type, nvpairs, eventString),
|
||||
m_devname(Value("devname"))
|
||||
{
|
||||
}
|
||||
|
||||
GeomEvent::GeomEvent(const GeomEvent &src)
|
||||
: Event(src),
|
||||
m_devname(src.m_devname)
|
||||
{
|
||||
}
|
||||
|
||||
/*--------------------------------- ZfsEvent ---------------------------------*/
|
||||
//- ZfsEvent Static Public Methods ---------------------------------------------
|
||||
Event *
|
||||
ZfsEvent::Builder(Event::Type type, NVPairMap &nvpairs,
|
||||
const string &eventString)
|
||||
{
|
||||
return (new ZfsEvent(type, nvpairs, eventString));
|
||||
}
|
||||
|
||||
//- ZfsEvent Virtual Public Methods --------------------------------------------
|
||||
Event *
|
||||
ZfsEvent::DeepCopy() const
|
||||
{
|
||||
return (new ZfsEvent(*this));
|
||||
}
|
||||
|
||||
bool
|
||||
ZfsEvent::DevName(std::string &name) const
|
||||
{
|
||||
return (false);
|
||||
}
|
||||
|
||||
//- ZfsEvent Protected Methods -------------------------------------------------
|
||||
ZfsEvent::ZfsEvent(Event::Type type, NVPairMap &nvpairs,
|
||||
const string &eventString)
|
||||
: Event(type, nvpairs, eventString),
|
||||
m_poolGUID(Guid(Value("pool_guid"))),
|
||||
m_vdevGUID(Guid(Value("vdev_guid")))
|
||||
{
|
||||
}
|
||||
|
||||
ZfsEvent::ZfsEvent(const ZfsEvent &src)
|
||||
: Event(src),
|
||||
m_poolGUID(src.m_poolGUID),
|
||||
m_vdevGUID(src.m_vdevGUID)
|
||||
{
|
||||
}
|
||||
|
||||
} // namespace DevdCtl
|
423
lib/libdevdctl/event.h
Normal file
423
lib/libdevdctl/event.h
Normal file
@ -0,0 +1,423 @@
|
||||
/*-
|
||||
* Copyright (c) 2011, 2012, 2013, 2016 Spectra Logic Corporation
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions, and the following disclaimer,
|
||||
* without modification.
|
||||
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
|
||||
* substantially similar to the "NO WARRANTY" disclaimer below
|
||||
* ("Disclaimer") and any redistribution must be conditioned upon
|
||||
* including a substantially similar Disclaimer requirement for further
|
||||
* binary redistribution.
|
||||
*
|
||||
* NO WARRANTY
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGES.
|
||||
*
|
||||
* Authors: Justin T. Gibbs (Spectra Logic Corporation)
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file devdctl_event.h
|
||||
*
|
||||
* \brief Class hierarchy used to express events received via
|
||||
* the devdctl API.
|
||||
*/
|
||||
|
||||
#ifndef _DEVDCTL_EVENT_H_
|
||||
#define _DEVDCTL_EVENT_H_
|
||||
|
||||
/*============================ Namespace Control =============================*/
|
||||
namespace DevdCtl
|
||||
{
|
||||
|
||||
/*=========================== Forward Declarations ===========================*/
|
||||
class EventFactory;
|
||||
|
||||
/*============================= Class Definitions ============================*/
|
||||
/*-------------------------------- NVPairMap ---------------------------------*/
|
||||
/**
|
||||
* NVPairMap is a specialization of the standard map STL container.
|
||||
*/
|
||||
typedef std::map<std::string, std::string> NVPairMap;
|
||||
|
||||
/*----------------------------------- Event ----------------------------------*/
|
||||
/**
|
||||
* \brief Container for the name => value pairs that comprise the content of
|
||||
* a device control event.
|
||||
*
|
||||
* All name => value data for events can be accessed via the Contains()
|
||||
* and Value() methods. name => value pairs for data not explicitly
|
||||
* received as a name => value pair are synthesized during parsing. For
|
||||
* example, ATTACH and DETACH events have "device-name" and "parent"
|
||||
* name => value pairs added.
|
||||
*/
|
||||
class Event
|
||||
{
|
||||
friend class EventFactory;
|
||||
|
||||
public:
|
||||
/** Event type */
|
||||
enum Type {
|
||||
/** Generic event notification. */
|
||||
NOTIFY = '!',
|
||||
|
||||
/** A driver was not found for this device. */
|
||||
NOMATCH = '?',
|
||||
|
||||
/** A bus device instance has been added. */
|
||||
ATTACH = '+',
|
||||
|
||||
/** A bus device instance has been removed. */
|
||||
DETACH = '-'
|
||||
};
|
||||
|
||||
/**
|
||||
* Factory method type to construct an Event given
|
||||
* the type of event and an NVPairMap populated from
|
||||
* the event string received from devd.
|
||||
*/
|
||||
typedef Event* (BuildMethod)(Type, NVPairMap &, const std::string &);
|
||||
|
||||
/** Generic Event object factory. */
|
||||
static BuildMethod Builder;
|
||||
|
||||
static Event *CreateEvent(const EventFactory &factory,
|
||||
const std::string &eventString);
|
||||
|
||||
/**
|
||||
* Returns the devname, if any, associated with the event
|
||||
*
|
||||
* \param name Devname, returned by reference
|
||||
* \return True iff the event contained a devname
|
||||
*/
|
||||
virtual bool DevName(std::string &name) const;
|
||||
|
||||
/**
|
||||
* Returns the absolute pathname of the device associated with this
|
||||
* event.
|
||||
*
|
||||
* \param name Devname, returned by reference
|
||||
* \return True iff the event contained a devname
|
||||
*/
|
||||
bool DevPath(std::string &path) const;
|
||||
|
||||
/**
|
||||
* Returns true iff this event refers to a disk device
|
||||
*/
|
||||
bool IsDiskDev() const;
|
||||
|
||||
/** Returns the physical path of the device, if any
|
||||
*
|
||||
* \param path Physical path, returned by reference
|
||||
* \return True iff the event contains a device with a physical
|
||||
* path
|
||||
*/
|
||||
bool PhysicalPath(std::string &path) const;
|
||||
|
||||
/**
|
||||
* Provide a user friendly string representation of an
|
||||
* event type.
|
||||
*
|
||||
* \param type The type of event to map to a string.
|
||||
*
|
||||
* \return A user friendly string representing the input type.
|
||||
*/
|
||||
static const char *TypeToString(Type type);
|
||||
|
||||
/**
|
||||
* Determine the availability of a name => value pair by name.
|
||||
*
|
||||
* \param name The key name to search for in this event instance.
|
||||
*
|
||||
* \return true if the specified key is available in this
|
||||
* event, otherwise false.
|
||||
*/
|
||||
bool Contains(const std::string &name) const;
|
||||
|
||||
/**
|
||||
* \param key The name of the key for which to retrieve its
|
||||
* associated value.
|
||||
*
|
||||
* \return A const reference to the string representing the
|
||||
* value associated with key.
|
||||
*
|
||||
* \note For key's with no registered value, the empty string
|
||||
* is returned.
|
||||
*/
|
||||
const std::string &Value(const std::string &key) const;
|
||||
|
||||
/**
|
||||
* Get the type of this event instance.
|
||||
*
|
||||
* \return The type of this event instance.
|
||||
*/
|
||||
Type GetType() const;
|
||||
|
||||
/**
|
||||
* Get the orginal DevdCtl event string for this event.
|
||||
*
|
||||
* \return The DevdCtl event string.
|
||||
*/
|
||||
const std::string &GetEventString() const;
|
||||
|
||||
/**
|
||||
* Convert the event instance into a string suitable for
|
||||
* printing to the console or emitting to syslog.
|
||||
*
|
||||
* \return A string of formatted event data.
|
||||
*/
|
||||
std::string ToString() const;
|
||||
|
||||
/**
|
||||
* Pretty-print this event instance to cout.
|
||||
*/
|
||||
void Print() const;
|
||||
|
||||
/**
|
||||
* Pretty-print this event instance to syslog.
|
||||
*
|
||||
* \param priority The logging priority/facility.
|
||||
* See syslog(3).
|
||||
*/
|
||||
void Log(int priority) const;
|
||||
|
||||
/**
|
||||
* Create and return a fully independent clone
|
||||
* of this event.
|
||||
*/
|
||||
virtual Event *DeepCopy() const;
|
||||
|
||||
/** Destructor */
|
||||
virtual ~Event();
|
||||
|
||||
/**
|
||||
* Interpret and perform any actions necessary to
|
||||
* consume the event.
|
||||
*
|
||||
* \return True if this event should be queued for later reevaluation
|
||||
*/
|
||||
virtual bool Process() const;
|
||||
|
||||
/**
|
||||
* Get the time that the event was created
|
||||
*/
|
||||
timeval GetTimestamp() const;
|
||||
|
||||
/**
|
||||
* Add a timestamp to the event string, if one does not already exist
|
||||
* TODO: make this an instance method that operates on the std::map
|
||||
* instead of the string. We must fix zfsd's CaseFile serialization
|
||||
* routines first, so that they don't need the raw event string.
|
||||
*
|
||||
* \param[in,out] eventString The devd event string to modify
|
||||
*/
|
||||
static void TimestampEventString(std::string &eventString);
|
||||
|
||||
/**
|
||||
* Access all parsed key => value pairs.
|
||||
*/
|
||||
const NVPairMap &GetMap() const;
|
||||
|
||||
protected:
|
||||
/** Table entries used to map a type to a user friendly string. */
|
||||
struct EventTypeRecord
|
||||
{
|
||||
Type m_type;
|
||||
const char *m_typeName;
|
||||
};
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* \param type The type of event to create.
|
||||
*/
|
||||
Event(Type type, NVPairMap &map, const std::string &eventString);
|
||||
|
||||
/** Deep copy constructor. */
|
||||
Event(const Event &src);
|
||||
|
||||
/** Always empty string returned when NVPairMap lookups fail. */
|
||||
static const std::string s_theEmptyString;
|
||||
|
||||
/** Unsorted table of event types. */
|
||||
static EventTypeRecord s_typeTable[];
|
||||
|
||||
/** The type of this event. */
|
||||
const Type m_type;
|
||||
|
||||
/**
|
||||
* Event attribute storage.
|
||||
*
|
||||
* \note Although stored by reference (since m_nvPairs can
|
||||
* never be NULL), the NVPairMap referenced by this field
|
||||
* is dynamically allocated and owned by this event object.
|
||||
* m_nvPairs must be deleted at event desctruction.
|
||||
*/
|
||||
NVPairMap &m_nvPairs;
|
||||
|
||||
/**
|
||||
* The unaltered event string, as received from devd, used to
|
||||
* create this event object.
|
||||
*/
|
||||
std::string m_eventString;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Ingest event data from the supplied string.
|
||||
*
|
||||
* \param[in] eventString The string of devd event data to parse.
|
||||
* \param[out] nvpairs Returns the parsed data
|
||||
*/
|
||||
static void ParseEventString(Type type, const std::string &eventString,
|
||||
NVPairMap &nvpairs);
|
||||
};
|
||||
|
||||
inline Event::Type
|
||||
Event::GetType() const
|
||||
{
|
||||
return (m_type);
|
||||
}
|
||||
|
||||
inline const std::string &
|
||||
Event::GetEventString() const
|
||||
{
|
||||
return (m_eventString);
|
||||
}
|
||||
|
||||
inline const NVPairMap &
|
||||
Event::GetMap() const
|
||||
{
|
||||
return (m_nvPairs);
|
||||
}
|
||||
|
||||
/*--------------------------------- EventList --------------------------------*/
|
||||
/**
|
||||
* EventList is a specialization of the standard list STL container.
|
||||
*/
|
||||
typedef std::list<Event *> EventList;
|
||||
|
||||
/*-------------------------------- DevfsEvent --------------------------------*/
|
||||
class DevfsEvent : public Event
|
||||
{
|
||||
public:
|
||||
/** Specialized Event object factory for Devfs events. */
|
||||
static BuildMethod Builder;
|
||||
|
||||
virtual Event *DeepCopy() const;
|
||||
|
||||
/**
|
||||
* Interpret and perform any actions necessary to
|
||||
* consume the event.
|
||||
* \return True if this event should be queued for later reevaluation
|
||||
*/
|
||||
virtual bool Process() const;
|
||||
|
||||
bool IsWholeDev() const;
|
||||
virtual bool DevName(std::string &name) const;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Given the device name of a disk, determine if the device
|
||||
* represents the whole device, not just a partition.
|
||||
*
|
||||
* \param devName Device name of disk device to test.
|
||||
*
|
||||
* \return True if the device name represents the whole device.
|
||||
* Otherwise false.
|
||||
*/
|
||||
static bool IsWholeDev(const std::string &devName);
|
||||
|
||||
/** DeepCopy Constructor. */
|
||||
DevfsEvent(const DevfsEvent &src);
|
||||
|
||||
/** Constructor */
|
||||
DevfsEvent(Type, NVPairMap &, const std::string &);
|
||||
};
|
||||
|
||||
/*--------------------------------- GeomEvent --------------------------------*/
|
||||
class GeomEvent : public Event
|
||||
{
|
||||
public:
|
||||
/** Specialized Event object factory for GEOM events. */
|
||||
static BuildMethod Builder;
|
||||
|
||||
virtual Event *DeepCopy() const;
|
||||
|
||||
virtual bool DevName(std::string &name) const;
|
||||
|
||||
const std::string &DeviceName() const;
|
||||
|
||||
protected:
|
||||
/** Constructor */
|
||||
GeomEvent(Type, NVPairMap &, const std::string &);
|
||||
|
||||
/** Deep copy constructor. */
|
||||
GeomEvent(const GeomEvent &src);
|
||||
|
||||
std::string m_devname;
|
||||
};
|
||||
|
||||
/*--------------------------------- ZfsEvent ---------------------------------*/
|
||||
class ZfsEvent : public Event
|
||||
{
|
||||
public:
|
||||
/** Specialized Event object factory for ZFS events. */
|
||||
static BuildMethod Builder;
|
||||
|
||||
virtual Event *DeepCopy() const;
|
||||
|
||||
virtual bool DevName(std::string &name) const;
|
||||
|
||||
const std::string &PoolName() const;
|
||||
Guid PoolGUID() const;
|
||||
Guid VdevGUID() const;
|
||||
|
||||
protected:
|
||||
/** Constructor */
|
||||
ZfsEvent(Type, NVPairMap &, const std::string &);
|
||||
|
||||
/** Deep copy constructor. */
|
||||
ZfsEvent(const ZfsEvent &src);
|
||||
|
||||
Guid m_poolGUID;
|
||||
Guid m_vdevGUID;
|
||||
};
|
||||
|
||||
//- ZfsEvent Inline Public Methods --------------------------------------------
|
||||
inline const std::string&
|
||||
ZfsEvent::PoolName() const
|
||||
{
|
||||
/* The pool name is reported as the subsystem of ZFS events. */
|
||||
return (Value("subsystem"));
|
||||
}
|
||||
|
||||
inline Guid
|
||||
ZfsEvent::PoolGUID() const
|
||||
{
|
||||
return (m_poolGUID);
|
||||
}
|
||||
|
||||
inline Guid
|
||||
ZfsEvent::VdevGUID() const
|
||||
{
|
||||
return (m_vdevGUID);
|
||||
}
|
||||
|
||||
} // namespace DevdCtl
|
||||
#endif /*_DEVDCTL_EVENT_H_ */
|
99
lib/libdevdctl/event_factory.cc
Normal file
99
lib/libdevdctl/event_factory.cc
Normal file
@ -0,0 +1,99 @@
|
||||
/*-
|
||||
* Copyright (c) 2013 Spectra Logic Corporation
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions, and the following disclaimer,
|
||||
* without modification.
|
||||
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
|
||||
* substantially similar to the "NO WARRANTY" disclaimer below
|
||||
* ("Disclaimer") and any redistribution must be conditioned upon
|
||||
* including a substantially similar Disclaimer requirement for further
|
||||
* binary redistribution.
|
||||
*
|
||||
* NO WARRANTY
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGES.
|
||||
*
|
||||
* Authors: Justin T. Gibbs (Spectra Logic Corporation)
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file event_factory.cc
|
||||
*/
|
||||
#include <sys/cdefs.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include "guid.h"
|
||||
#include "event.h"
|
||||
#include "event_factory.h"
|
||||
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
/*================================== Macros ==================================*/
|
||||
#define NUM_ELEMENTS(x) (sizeof(x) / sizeof(*x))
|
||||
|
||||
/*============================ Namespace Control =============================*/
|
||||
namespace DevdCtl
|
||||
{
|
||||
|
||||
/*=========================== Class Implementations ==========================*/
|
||||
/*------------------------------- EventFactory -------------------------------*/
|
||||
//- Event Public Methods -------------------------------------------------------
|
||||
EventFactory::EventFactory(Event::BuildMethod *defaultBuildMethod)
|
||||
: m_defaultBuildMethod(defaultBuildMethod)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
EventFactory::UpdateRegistry(Record regEntries[], size_t numEntries)
|
||||
{
|
||||
EventFactory::Record *rec(regEntries);
|
||||
EventFactory::Record *lastRec(rec + numEntries - 1);
|
||||
|
||||
for (; rec <= lastRec; rec++) {
|
||||
Key key(rec->m_type, rec->m_subsystem);
|
||||
|
||||
if (rec->m_buildMethod == NULL)
|
||||
m_registry.erase(key);
|
||||
else
|
||||
m_registry[key] = rec->m_buildMethod;
|
||||
}
|
||||
}
|
||||
|
||||
Event *
|
||||
EventFactory::Build(Event::Type type, NVPairMap &nvpairs,
|
||||
const std::string eventString) const
|
||||
{
|
||||
Key key(type, nvpairs["system"]);
|
||||
Event::BuildMethod *buildMethod(m_defaultBuildMethod);
|
||||
|
||||
Registry::const_iterator foundMethod(m_registry.find(key));
|
||||
if (foundMethod != m_registry.end())
|
||||
buildMethod = foundMethod->second;
|
||||
|
||||
if (buildMethod == NULL) {
|
||||
delete &nvpairs;
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
return (buildMethod(type, nvpairs, eventString));
|
||||
}
|
||||
|
||||
} // namespace DevdCtl
|
94
lib/libdevdctl/event_factory.h
Normal file
94
lib/libdevdctl/event_factory.h
Normal file
@ -0,0 +1,94 @@
|
||||
/*-
|
||||
* Copyright (c) 2013 Spectra Logic Corporation
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions, and the following disclaimer,
|
||||
* without modification.
|
||||
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
|
||||
* substantially similar to the "NO WARRANTY" disclaimer below
|
||||
* ("Disclaimer") and any redistribution must be conditioned upon
|
||||
* including a substantially similar Disclaimer requirement for further
|
||||
* binary redistribution.
|
||||
*
|
||||
* NO WARRANTY
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGES.
|
||||
*
|
||||
* Authors: Justin T. Gibbs (Spectra Logic Corporation)
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file devdctl_event_factory.h
|
||||
*/
|
||||
|
||||
#ifndef _DEVDCTL_EVENT_FACTORY_H_
|
||||
#define _DEVDCTL_EVENT_FACTORY_H_
|
||||
|
||||
/*============================ Namespace Control =============================*/
|
||||
namespace DevdCtl
|
||||
{
|
||||
|
||||
/*============================= Class Definitions ============================*/
|
||||
/*------------------------------- EventFactory -------------------------------*/
|
||||
/**
|
||||
* \brief Container for "event type" => "event object" creation methods.
|
||||
*/
|
||||
class EventFactory
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Event creation handlers are matched by event type and a
|
||||
* string representing the system emitting the event.
|
||||
*/
|
||||
typedef std::pair<Event::Type, std::string> Key;
|
||||
|
||||
/** Map type for Factory method lookups. */
|
||||
typedef std::map<Key, Event::BuildMethod *> Registry;
|
||||
|
||||
/** Table record of factory methods to add to our registry. */
|
||||
struct Record
|
||||
{
|
||||
Event::Type m_type;
|
||||
const char *m_subsystem;
|
||||
Event::BuildMethod *m_buildMethod;
|
||||
};
|
||||
|
||||
const Registry &GetRegistry() const;
|
||||
Event *Build(Event::Type type, NVPairMap &nvpairs,
|
||||
const std::string eventString) const;
|
||||
|
||||
EventFactory(Event::BuildMethod *defaultBuildMethod = NULL);
|
||||
|
||||
void UpdateRegistry(Record regEntries[], size_t numEntries);
|
||||
|
||||
|
||||
protected:
|
||||
/** Registry of event factory methods providing O(log(n)) lookup. */
|
||||
Registry m_registry;
|
||||
|
||||
Event::BuildMethod *m_defaultBuildMethod;
|
||||
};
|
||||
|
||||
inline const EventFactory::Registry &
|
||||
EventFactory::GetRegistry() const
|
||||
{
|
||||
return (m_registry);
|
||||
}
|
||||
|
||||
} // namespace DevdCtl
|
||||
#endif /*_DEVDCTL_EVENT_FACTORY_H_ */
|
125
lib/libdevdctl/exception.cc
Normal file
125
lib/libdevdctl/exception.cc
Normal file
@ -0,0 +1,125 @@
|
||||
/*-
|
||||
* Copyright (c) 2011, 2012, 2013, 2014 Spectra Logic Corporation
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions, and the following disclaimer,
|
||||
* without modification.
|
||||
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
|
||||
* substantially similar to the "NO WARRANTY" disclaimer below
|
||||
* ("Disclaimer") and any redistribution must be conditioned upon
|
||||
* including a substantially similar Disclaimer requirement for further
|
||||
* binary redistribution.
|
||||
*
|
||||
* NO WARRANTY
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGES.
|
||||
*
|
||||
* Authors: Justin T. Gibbs (Spectra Logic Corporation)
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file exception.cc
|
||||
*/
|
||||
#include <sys/cdefs.h>
|
||||
|
||||
#include <syslog.h>
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdarg>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include "exception.h"
|
||||
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
/*============================ Namespace Control =============================*/
|
||||
using std::string;
|
||||
using std::stringstream;
|
||||
using std::endl;
|
||||
namespace DevdCtl
|
||||
{
|
||||
|
||||
/*=========================== Class Implementations ==========================*/
|
||||
/*--------------------------------- Exception --------------------------------*/
|
||||
void
|
||||
Exception::FormatLog(const char *fmt, va_list ap)
|
||||
{
|
||||
char buf[256];
|
||||
|
||||
vsnprintf(buf, sizeof(buf), fmt, ap);
|
||||
m_log = buf;
|
||||
}
|
||||
|
||||
Exception::Exception(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
FormatLog(fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
Exception::Exception()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
Exception::Log() const
|
||||
{
|
||||
syslog(LOG_ERR, "%s", m_log.c_str());
|
||||
}
|
||||
|
||||
/*------------------------------ ParseException ------------------------------*/
|
||||
//- ParseException Inline Public Methods ---------------------------------------
|
||||
ParseException::ParseException(Type type, const std::string &parsedBuffer,
|
||||
size_t offset)
|
||||
: Exception(),
|
||||
m_type(type),
|
||||
m_parsedBuffer(parsedBuffer),
|
||||
m_offset(offset)
|
||||
{
|
||||
stringstream logstream;
|
||||
|
||||
logstream << "Parsing ";
|
||||
|
||||
switch (Type()) {
|
||||
case INVALID_FORMAT:
|
||||
logstream << "invalid format ";
|
||||
break;
|
||||
case DISCARDED_EVENT_TYPE:
|
||||
logstream << "discarded event ";
|
||||
break;
|
||||
case UNKNOWN_EVENT_TYPE:
|
||||
logstream << "unknown event ";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
logstream << "exception on buffer: \'";
|
||||
if (GetOffset() == 0) {
|
||||
logstream << m_parsedBuffer << '\'' << endl;
|
||||
} else {
|
||||
string markedBuffer(m_parsedBuffer);
|
||||
|
||||
markedBuffer.insert(GetOffset(), "<HERE-->");
|
||||
logstream << markedBuffer << '\'' << endl;
|
||||
}
|
||||
|
||||
GetString() = logstream.str();
|
||||
}
|
||||
|
||||
} // namespace DevdCtl
|
168
lib/libdevdctl/exception.h
Normal file
168
lib/libdevdctl/exception.h
Normal file
@ -0,0 +1,168 @@
|
||||
/*-
|
||||
* Copyright (c) 2011, 2012, 2013 Spectra Logic Corporation
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions, and the following disclaimer,
|
||||
* without modification.
|
||||
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
|
||||
* substantially similar to the "NO WARRANTY" disclaimer below
|
||||
* ("Disclaimer") and any redistribution must be conditioned upon
|
||||
* including a substantially similar Disclaimer requirement for further
|
||||
* binary redistribution.
|
||||
*
|
||||
* NO WARRANTY
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGES.
|
||||
*
|
||||
* Authors: Justin T. Gibbs (Spectra Logic Corporation)
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file zfsd_exception.h
|
||||
*
|
||||
* Definition of the ZfsdException class hierarchy. All exceptions
|
||||
* explicitly thrown by Zfsd are defined here.
|
||||
*/
|
||||
#ifndef _DEVDCTL_EXCEPTION_H_
|
||||
#define _DEVDCTL_EXCEPTION_H_
|
||||
|
||||
/*============================ Namespace Control =============================*/
|
||||
namespace DevdCtl
|
||||
{
|
||||
|
||||
/*============================= Class Definitions ============================*/
|
||||
|
||||
/*--------------------------------- Exception --------------------------------*/
|
||||
/**
|
||||
* \brief Class allowing unified reporting/logging of exceptional events.
|
||||
*/
|
||||
class Exception
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* \brief Exception constructor allowing arbitrary string
|
||||
* data to be reported.
|
||||
*
|
||||
* \param fmt Printf-like string format specifier.
|
||||
*/
|
||||
Exception(const char *fmt, ...);
|
||||
|
||||
/**
|
||||
* \brief Augment/Modify a Exception's string data.
|
||||
*/
|
||||
std::string& GetString();
|
||||
|
||||
/**
|
||||
* \brief Emit exception data to syslog(3).
|
||||
*/
|
||||
virtual void Log() const;
|
||||
|
||||
protected:
|
||||
Exception();
|
||||
|
||||
/**
|
||||
* \brief Convert exception string format and arguments provided
|
||||
* in event constructors into a linear string.
|
||||
*/
|
||||
void FormatLog(const char *fmt, va_list ap);
|
||||
|
||||
std::string m_log;
|
||||
};
|
||||
|
||||
inline std::string &
|
||||
Exception::GetString()
|
||||
{
|
||||
return (m_log);
|
||||
}
|
||||
|
||||
/*------------------------------ ParseException ------------------------------*/
|
||||
/**
|
||||
* Exception thrown when an event string is not converted to an actionable
|
||||
* Event object.
|
||||
*/
|
||||
class ParseException : public Exception
|
||||
{
|
||||
public:
|
||||
enum Type
|
||||
{
|
||||
/** Improperly formatted event string encountered. */
|
||||
INVALID_FORMAT,
|
||||
|
||||
/** No handlers for this event type. */
|
||||
DISCARDED_EVENT_TYPE,
|
||||
|
||||
/** Unhandled event type. */
|
||||
UNKNOWN_EVENT_TYPE
|
||||
};
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* \param type The type of this exception.
|
||||
* \param parsedBuffer The parsing buffer active at the time of
|
||||
* the exception.
|
||||
* \param offset The location in the parse buffer where this
|
||||
* exception occurred.
|
||||
*/
|
||||
ParseException(Type type, const std::string &parsedBuffer,
|
||||
size_t offset = 0);
|
||||
|
||||
/**
|
||||
* Accessor
|
||||
*
|
||||
* \return The classification for this exception.
|
||||
*/
|
||||
Type GetType() const;
|
||||
|
||||
/**
|
||||
* Accessor
|
||||
*
|
||||
* \return The offset into the event string where this exception
|
||||
* occurred.
|
||||
*/
|
||||
size_t GetOffset() const;
|
||||
|
||||
private:
|
||||
/** The type of this exception. */
|
||||
Type m_type;
|
||||
|
||||
/** The parsing buffer that was active at the time of the exception. */
|
||||
const std::string m_parsedBuffer;
|
||||
|
||||
/**
|
||||
* The offset into the event string buffer from where this
|
||||
* exception was triggered.
|
||||
*/
|
||||
size_t m_offset;
|
||||
};
|
||||
|
||||
//- ParseException Inline Const Public Methods ---------------------------------
|
||||
inline ParseException::Type
|
||||
ParseException::GetType() const
|
||||
{
|
||||
return (m_type);
|
||||
}
|
||||
|
||||
inline size_t
|
||||
ParseException::GetOffset() const
|
||||
{
|
||||
return (m_offset);
|
||||
}
|
||||
|
||||
} // namespace DevdCtl
|
||||
#endif /* _DEVDCTL_EXCEPTION_H_ */
|
82
lib/libdevdctl/guid.cc
Normal file
82
lib/libdevdctl/guid.cc
Normal file
@ -0,0 +1,82 @@
|
||||
/*-
|
||||
* Copyright (c) 2012, 2013 Spectra Logic Corporation
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions, and the following disclaimer,
|
||||
* without modification.
|
||||
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
|
||||
* substantially similar to the "NO WARRANTY" disclaimer below
|
||||
* ("Disclaimer") and any redistribution must be conditioned upon
|
||||
* including a substantially similar Disclaimer requirement for further
|
||||
* binary redistribution.
|
||||
*
|
||||
* NO WARRANTY
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGES.
|
||||
*
|
||||
* Authors: Alan Somers (Spectra Logic Corporation)
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file guid.cc
|
||||
*
|
||||
* Implementation of the Guid class.
|
||||
*/
|
||||
#include <sys/cdefs.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <limits.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include "guid.h"
|
||||
|
||||
__FBSDID("$FreeBSD$");
|
||||
/*============================ Namespace Control =============================*/
|
||||
using std::string;
|
||||
namespace DevdCtl
|
||||
{
|
||||
|
||||
/*=========================== Class Implementations ==========================*/
|
||||
/*----------------------------------- Guid -----------------------------------*/
|
||||
Guid::Guid(const string &guidString)
|
||||
{
|
||||
if (guidString.empty()) {
|
||||
m_GUID = INVALID_GUID;
|
||||
} else {
|
||||
/*
|
||||
* strtoumax() returns zero on conversion failure
|
||||
* which nicely matches our choice for INVALID_GUID.
|
||||
*/
|
||||
m_GUID = (uint64_t)strtoumax(guidString.c_str(), NULL, 0);
|
||||
}
|
||||
}
|
||||
|
||||
std::ostream&
|
||||
operator<< (std::ostream& out, Guid g)
|
||||
{
|
||||
if (g.IsValid())
|
||||
out << (uint64_t)g;
|
||||
else
|
||||
out << "None";
|
||||
return (out);
|
||||
}
|
||||
|
||||
} // namespace DevdCtl
|
143
lib/libdevdctl/guid.h
Normal file
143
lib/libdevdctl/guid.h
Normal file
@ -0,0 +1,143 @@
|
||||
/*-
|
||||
* Copyright (c) 2012, 2013 Spectra Logic Corporation
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions, and the following disclaimer,
|
||||
* without modification.
|
||||
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
|
||||
* substantially similar to the "NO WARRANTY" disclaimer below
|
||||
* ("Disclaimer") and any redistribution must be conditioned upon
|
||||
* including a substantially similar Disclaimer requirement for further
|
||||
* binary redistribution.
|
||||
*
|
||||
* NO WARRANTY
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGES.
|
||||
*
|
||||
* Authors: Alan Somers (Spectra Logic Corporation)
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file devdctl_guid.h
|
||||
*
|
||||
* Definition of the Guid class.
|
||||
*/
|
||||
#ifndef _DEVDCTL_GUID_H_
|
||||
#define _DEVDCTL_GUID_H_
|
||||
|
||||
/*============================ Namespace Control =============================*/
|
||||
namespace DevdCtl
|
||||
{
|
||||
|
||||
/*============================= Class Definitions ============================*/
|
||||
/*----------------------------------- Guid -----------------------------------*/
|
||||
/**
|
||||
* \brief Object that represents guids.
|
||||
*
|
||||
* It can generally be manipulated as a uint64_t, but with a special
|
||||
* value INVALID_GUID that does not equal any valid guid.
|
||||
*
|
||||
* As of this writing, this class is only used to represent ZFS
|
||||
* guids in events and spa_generate_guid() in spa_misc.c explicitly
|
||||
* refuses to return a guid of 0. So this class uses 0 as the value
|
||||
* for INVALID_GUID. In the future, if 0 is allowed to be a valid
|
||||
* guid, the implementation of this class must change.
|
||||
*/
|
||||
class Guid
|
||||
{
|
||||
public:
|
||||
/* Constructors */
|
||||
Guid();
|
||||
Guid(uint64_t guid);
|
||||
Guid(const std::string &guid);
|
||||
|
||||
/* Assignment */
|
||||
Guid& operator=(const Guid& rhs);
|
||||
|
||||
/* Test the validity of this guid. */
|
||||
bool IsValid() const;
|
||||
|
||||
/* Comparison to other Guid operators */
|
||||
bool operator==(const Guid& rhs) const;
|
||||
bool operator!=(const Guid& rhs) const;
|
||||
|
||||
/* Integer conversion operators */
|
||||
operator uint64_t() const;
|
||||
operator bool() const;
|
||||
|
||||
static const uint64_t INVALID_GUID = 0;
|
||||
protected:
|
||||
/* The integer value of the GUID. */
|
||||
uint64_t m_GUID;
|
||||
};
|
||||
|
||||
//- Guid Inline Public Methods ------------------------------------------------
|
||||
inline
|
||||
Guid::Guid()
|
||||
: m_GUID(INVALID_GUID)
|
||||
{
|
||||
}
|
||||
|
||||
inline
|
||||
Guid::Guid(uint64_t guid)
|
||||
: m_GUID(guid)
|
||||
{
|
||||
}
|
||||
|
||||
inline Guid&
|
||||
Guid::operator=(const Guid &rhs)
|
||||
{
|
||||
m_GUID = rhs.m_GUID;
|
||||
return (*this);
|
||||
}
|
||||
|
||||
inline bool
|
||||
Guid::IsValid() const
|
||||
{
|
||||
return (m_GUID != INVALID_GUID);
|
||||
}
|
||||
|
||||
inline bool
|
||||
Guid::operator==(const Guid& rhs) const
|
||||
{
|
||||
return (m_GUID == rhs.m_GUID);
|
||||
}
|
||||
|
||||
inline bool
|
||||
Guid::operator!=(const Guid& rhs) const
|
||||
{
|
||||
return (m_GUID != rhs.m_GUID);
|
||||
}
|
||||
|
||||
inline
|
||||
Guid::operator uint64_t() const
|
||||
{
|
||||
return (m_GUID);
|
||||
}
|
||||
|
||||
inline
|
||||
Guid::operator bool() const
|
||||
{
|
||||
return (m_GUID != INVALID_GUID);
|
||||
}
|
||||
|
||||
/** Convert the GUID into its string representation */
|
||||
std::ostream& operator<< (std::ostream& out, Guid g);
|
||||
|
||||
} // namespace DevdCtl
|
||||
#endif /* _DEVDCTL_GUID_H_ */
|
21
lib/libdevdctl/tests/Makefile
Normal file
21
lib/libdevdctl/tests/Makefile
Normal file
@ -0,0 +1,21 @@
|
||||
# $FreeBSD$
|
||||
|
||||
TESTSDIR= ${TESTSBASE}/lib/libdevdctl
|
||||
|
||||
.PATH: ${.CURDIR}/..
|
||||
|
||||
PLAIN_TESTS_CXX= libdevdctl_unittest
|
||||
|
||||
SRCS.libdevdctl_unittest+= event_factory.cc \
|
||||
libdevdctl_unittest.cc \
|
||||
event.cc exception.cc \
|
||||
guid.cc
|
||||
CFLAGS.libdevdctl_unittest+= -I ${LOCALBASE}/include -D_THREAD_SAFE -pthread
|
||||
DPADD.libdevdctl_unittest+= ${LIBDEVDCTL}
|
||||
LDADD.libdevdctl_unittest+= -L ${LOCALBASE}/lib -D_THREAD_SAFE -pthread -lgtest -lgtest_main
|
||||
|
||||
# Googletest options
|
||||
LOCALBASE?= /usr/local
|
||||
|
||||
WARNS?= 3
|
||||
.include <bsd.test.mk>
|
138
lib/libdevdctl/tests/libdevdctl_unittest.cc
Normal file
138
lib/libdevdctl/tests/libdevdctl_unittest.cc
Normal file
@ -0,0 +1,138 @@
|
||||
/*-
|
||||
* Copyright (c) 2016 Spectra Logic Corporation
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions, and the following disclaimer,
|
||||
* without modification.
|
||||
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
|
||||
* substantially similar to the "NO WARRANTY" disclaimer below
|
||||
* ("Disclaimer") and any redistribution must be conditioned upon
|
||||
* including a substantially similar Disclaimer requirement for further
|
||||
* binary redistribution.
|
||||
*
|
||||
* NO WARRANTY
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGES.
|
||||
*
|
||||
* Authors: Alan Somers (Spectra Logic Corporation)
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include <devdctl/guid.h>
|
||||
#include <devdctl/event.h>
|
||||
#include <devdctl/event_factory.h>
|
||||
|
||||
using namespace DevdCtl;
|
||||
using namespace std;
|
||||
using namespace testing;
|
||||
|
||||
#define REGISTRY_SIZE 2
|
||||
|
||||
struct DevNameTestParams
|
||||
{
|
||||
const char* evs;
|
||||
bool is_disk;
|
||||
const char* devname;
|
||||
};
|
||||
|
||||
class DevNameTest : public TestWithParam<DevNameTestParams>{
|
||||
protected:
|
||||
virtual void SetUp()
|
||||
{
|
||||
m_factory = new EventFactory();
|
||||
m_factory->UpdateRegistry(s_registry, REGISTRY_SIZE);
|
||||
}
|
||||
|
||||
virtual void TearDown()
|
||||
{
|
||||
if (m_ev) delete m_ev;
|
||||
if (m_factory) delete m_factory;
|
||||
}
|
||||
|
||||
EventFactory *m_factory;
|
||||
Event *m_ev;
|
||||
static EventFactory::Record s_registry[REGISTRY_SIZE];
|
||||
};
|
||||
|
||||
DevdCtl::EventFactory::Record DevNameTest::s_registry[REGISTRY_SIZE] = {
|
||||
{ Event::NOTIFY, "DEVFS", &DevfsEvent::Builder },
|
||||
{ Event::NOTIFY, "GEOM", &GeomEvent::Builder }
|
||||
};
|
||||
|
||||
TEST_P(DevNameTest, TestDevname) {
|
||||
std::string devname;
|
||||
DevNameTestParams param = GetParam();
|
||||
|
||||
string evString(param.evs);
|
||||
m_ev = Event::CreateEvent(*m_factory, evString);
|
||||
m_ev->DevName(devname);
|
||||
EXPECT_STREQ(param.devname, devname.c_str());
|
||||
}
|
||||
|
||||
TEST_P(DevNameTest, TestIsDiskDev) {
|
||||
DevNameTestParams param = GetParam();
|
||||
|
||||
string evString(param.evs);
|
||||
m_ev = Event::CreateEvent(*m_factory, evString);
|
||||
EXPECT_EQ(param.is_disk, m_ev->IsDiskDev());
|
||||
}
|
||||
|
||||
/* TODO: clean this up using C++-11 uniform initializers */
|
||||
INSTANTIATE_TEST_CASE_P(IsDiskDevTestInstantiation, DevNameTest, Values(
|
||||
(DevNameTestParams){
|
||||
.evs = "!system=DEVFS subsystem=CDEV type=CREATE cdev=da6\n",
|
||||
.is_disk = true, .devname = "da6"},
|
||||
(DevNameTestParams){.is_disk = false, .devname = "cuau0",
|
||||
.evs = "!system=DEVFS subsystem=CDEV type=CREATE cdev=cuau0\n"},
|
||||
(DevNameTestParams){.is_disk = true, .devname = "ada6",
|
||||
.evs = "!system=DEVFS subsystem=CDEV type=CREATE cdev=ada6\n"},
|
||||
(DevNameTestParams){.is_disk = true, .devname = "da6p1",
|
||||
.evs = "!system=DEVFS subsystem=CDEV type=CREATE cdev=da6p1\n"},
|
||||
(DevNameTestParams){.is_disk = true, .devname = "ada6p1",
|
||||
.evs = "!system=DEVFS subsystem=CDEV type=CREATE cdev=ada6p1\n"},
|
||||
(DevNameTestParams){.is_disk = true, .devname = "da6s0p1",
|
||||
.evs = "!system=DEVFS subsystem=CDEV type=CREATE cdev=da6s0p1\n"},
|
||||
(DevNameTestParams){.is_disk = true, .devname = "ada6s0p1",
|
||||
.evs = "!system=DEVFS subsystem=CDEV type=CREATE cdev=ada6s0p1\n"},
|
||||
/*
|
||||
* Test physical path nodes. These are currently all set to false since
|
||||
* physical path nodes are implemented with symlinks, and most CAM and
|
||||
* ZFS operations can't use symlinked device nodes
|
||||
*/
|
||||
/* A SpectraBSD-style physical path node*/
|
||||
(DevNameTestParams){.is_disk = false, .devname = "enc@50030480019f53fd/elmtype@array_device/slot@18/da",
|
||||
.evs = "!system=DEVFS subsystem=CDEV type=CREATE cdev=enc@50030480019f53fd/elmtype@array_device/slot@18/da\n"},
|
||||
(DevNameTestParams){.is_disk = false, .devname = "enc@50030480019f53fd/elmtype@array_device/slot@18/pass",
|
||||
.evs = "!system=DEVFS subsystem=CDEV type=CREATE cdev=enc@50030480019f53fd/elmtype@array_device/slot@18/pass\n"},
|
||||
/* A FreeBSD-style physical path node */
|
||||
(DevNameTestParams){.is_disk = true, .devname = "enc@n50030480019f53fd/type@0/slot@18/elmdesc@ArrayDevice18/da6",
|
||||
.evs = "!system=DEVFS subsystem=CDEV type=CREATE cdev=enc@n50030480019f53fd/type@0/slot@18/elmdesc@ArrayDevice18/da6\n"},
|
||||
(DevNameTestParams){.is_disk = false, .devname = "enc@n50030480019f53fd/type@0/slot@18/elmdesc@ArrayDevice18/pass6",
|
||||
.evs = "!system=DEVFS subsystem=CDEV type=CREATE cdev=enc@n50030480019f53fd/type@0/slot@18/elmdesc@ArrayDevice18/pass6\n"},
|
||||
|
||||
/*
|
||||
* Test some GEOM events
|
||||
*/
|
||||
(DevNameTestParams){.is_disk = true, .devname = "da5",
|
||||
.evs = "!system=GEOM subsystem=disk type=GEOM::physpath devname=da5\n"})
|
||||
);
|
@ -49,6 +49,7 @@ LIBCXGB4?= ${DESTDIR}${LIBDIR}/libcxgb4.a
|
||||
LIBCXXRT?= ${DESTDIR}${LIBDIR}/libcxxrt.a
|
||||
LIBC_PIC?= ${DESTDIR}${LIBDIR}/libc_pic.a
|
||||
LIBDEVCTL?= ${DESTDIR}${LIBDIR}/libdevctl.a
|
||||
LIBDEVDCTL?= ${DESTDIR}${LIBDIR}/libdevdctl.a
|
||||
LIBDEVINFO?= ${DESTDIR}${LIBDIR}/libdevinfo.a
|
||||
LIBDEVSTAT?= ${DESTDIR}${LIBDIR}/libdevstat.a
|
||||
LIBDIALOG?= ${DESTDIR}${LIBDIR}/libdialog.a
|
||||
|
@ -16,6 +16,7 @@ _PRIVATELIBS= \
|
||||
atf_c \
|
||||
atf_cxx \
|
||||
bsdstat \
|
||||
devdctl \
|
||||
event \
|
||||
heimipcc \
|
||||
heimipcs \
|
||||
@ -82,6 +83,7 @@ _LIBRARIES= \
|
||||
cuse \
|
||||
cxxrt \
|
||||
devctl \
|
||||
devdctl \
|
||||
devinfo \
|
||||
devstat \
|
||||
dialog \
|
||||
|
@ -3366,19 +3366,6 @@ vdev_set_state(vdev_t *vd, boolean_t isopen, vdev_state_t state, vdev_aux_t aux)
|
||||
vd->vdev_ops->vdev_op_leaf)
|
||||
vd->vdev_ops->vdev_op_close(vd);
|
||||
|
||||
/*
|
||||
* If we have brought this vdev back into service, we need
|
||||
* to notify fmd so that it can gracefully repair any outstanding
|
||||
* cases due to a missing device. We do this in all cases, even those
|
||||
* that probably don't correlate to a repaired fault. This is sure to
|
||||
* catch all cases, and we let the zfs-retire agent sort it out. If
|
||||
* this is a transient state it's OK, as the retire agent will
|
||||
* double-check the state of the vdev before repairing it.
|
||||
*/
|
||||
if (state == VDEV_STATE_HEALTHY && vd->vdev_ops->vdev_op_leaf &&
|
||||
vd->vdev_prevstate != state)
|
||||
zfs_post_state_change(spa, vd);
|
||||
|
||||
if (vd->vdev_removed &&
|
||||
state == VDEV_STATE_CANT_OPEN &&
|
||||
(aux == VDEV_AUX_OPEN_FAILED || vd->vdev_checkremove)) {
|
||||
@ -3459,6 +3446,16 @@ vdev_set_state(vdev_t *vd, boolean_t isopen, vdev_state_t state, vdev_aux_t aux)
|
||||
vd->vdev_removed = B_FALSE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Notify the fmd of the state change. Be verbose and post
|
||||
* notifications even for stuff that's not important; the fmd agent can
|
||||
* sort it out. Don't emit state change events for non-leaf vdevs since
|
||||
* they can't change state on their own. The FMD can check their state
|
||||
* if it wants to when it sees that a leaf vdev had a state change.
|
||||
*/
|
||||
if (vd->vdev_ops->vdev_op_leaf)
|
||||
zfs_post_state_change(spa, vd);
|
||||
|
||||
if (!isopen && vd->vdev_parent)
|
||||
vdev_propagate_state(vd->vdev_parent);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user