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
1766eeafce
commit
7a0c41d5d7
@ -7,6 +7,7 @@ SUBDIR= ${_dtrace} \
|
|||||||
${_plockstat} \
|
${_plockstat} \
|
||||||
${_tests} \
|
${_tests} \
|
||||||
${_zdb} \
|
${_zdb} \
|
||||||
|
${_zfsd} \
|
||||||
${_zhack}
|
${_zhack}
|
||||||
|
|
||||||
.if ${MK_TESTS} != "no"
|
.if ${MK_TESTS} != "no"
|
||||||
@ -18,6 +19,9 @@ _tests= tests
|
|||||||
_zdb= zdb
|
_zdb= zdb
|
||||||
_zhack= zhack
|
_zhack= zhack
|
||||||
.endif
|
.endif
|
||||||
|
. if ${MK_CXX} != "no"
|
||||||
|
_zfsd= zfsd
|
||||||
|
. endif
|
||||||
.endif
|
.endif
|
||||||
|
|
||||||
.if ${MACHINE_ARCH} == "amd64" || ${MACHINE_ARCH} == "i386"
|
.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 support
|
||||||
zfs_enable="NO" # Set to YES to automatically mount ZFS file systems
|
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.
|
gptboot_enable="YES" # GPT boot success/failure reporting.
|
||||||
|
|
||||||
# Experimental - test before enabling
|
# Experimental - test before enabling
|
||||||
|
@ -155,6 +155,8 @@
|
|||||||
wi
|
wi
|
||||||
..
|
..
|
||||||
..
|
..
|
||||||
|
devdctl
|
||||||
|
..
|
||||||
edit
|
edit
|
||||||
readline
|
readline
|
||||||
..
|
..
|
||||||
|
@ -48,8 +48,6 @@
|
|||||||
cddl
|
cddl
|
||||||
lib
|
lib
|
||||||
..
|
..
|
||||||
sbin
|
|
||||||
..
|
|
||||||
usr.bin
|
usr.bin
|
||||||
..
|
..
|
||||||
usr.sbin
|
usr.sbin
|
||||||
@ -215,6 +213,8 @@
|
|||||||
..
|
..
|
||||||
..
|
..
|
||||||
..
|
..
|
||||||
|
zfsd
|
||||||
|
..
|
||||||
..
|
..
|
||||||
..
|
..
|
||||||
etc
|
etc
|
||||||
@ -308,6 +308,8 @@
|
|||||||
..
|
..
|
||||||
libcrypt
|
libcrypt
|
||||||
..
|
..
|
||||||
|
libdevdctl
|
||||||
|
..
|
||||||
libmp
|
libmp
|
||||||
..
|
..
|
||||||
libnv
|
libnv
|
||||||
|
@ -54,6 +54,10 @@
|
|||||||
..
|
..
|
||||||
portsnap
|
portsnap
|
||||||
..
|
..
|
||||||
|
zfsd
|
||||||
|
cases
|
||||||
|
..
|
||||||
|
..
|
||||||
..
|
..
|
||||||
empty mode=0555 flags=schg
|
empty mode=0555 flags=schg
|
||||||
..
|
..
|
||||||
|
@ -123,7 +123,7 @@ FILES= DAEMON \
|
|||||||
ypserv \
|
ypserv \
|
||||||
ypset \
|
ypset \
|
||||||
ypupdated \
|
ypupdated \
|
||||||
ypxfrd \
|
ypxfrd
|
||||||
|
|
||||||
.if ${MK_ACCT} != "no"
|
.if ${MK_ACCT} != "no"
|
||||||
FILESGROUPS+= ACCT
|
FILESGROUPS+= ACCT
|
||||||
@ -308,6 +308,7 @@ FILES+= wpa_supplicant
|
|||||||
.if ${MK_ZFS} != "no"
|
.if ${MK_ZFS} != "no"
|
||||||
FILESGROUPS+= ZFS
|
FILESGROUPS+= ZFS
|
||||||
ZFS+= zfs
|
ZFS+= zfs
|
||||||
|
ZFS+= zfsd
|
||||||
ZFS+= zvol
|
ZFS+= zvol
|
||||||
ZFSPACKAGE= zfs
|
ZFSPACKAGE= zfs
|
||||||
.endif
|
.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 \
|
libcompat \
|
||||||
libcrypt \
|
libcrypt \
|
||||||
libdevctl \
|
libdevctl \
|
||||||
|
libdevdctl \
|
||||||
libdevinfo \
|
libdevinfo \
|
||||||
libdevstat \
|
libdevstat \
|
||||||
libdpv \
|
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
|
LIBCXXRT?= ${DESTDIR}${LIBDIR}/libcxxrt.a
|
||||||
LIBC_PIC?= ${DESTDIR}${LIBDIR}/libc_pic.a
|
LIBC_PIC?= ${DESTDIR}${LIBDIR}/libc_pic.a
|
||||||
LIBDEVCTL?= ${DESTDIR}${LIBDIR}/libdevctl.a
|
LIBDEVCTL?= ${DESTDIR}${LIBDIR}/libdevctl.a
|
||||||
|
LIBDEVDCTL?= ${DESTDIR}${LIBDIR}/libdevdctl.a
|
||||||
LIBDEVINFO?= ${DESTDIR}${LIBDIR}/libdevinfo.a
|
LIBDEVINFO?= ${DESTDIR}${LIBDIR}/libdevinfo.a
|
||||||
LIBDEVSTAT?= ${DESTDIR}${LIBDIR}/libdevstat.a
|
LIBDEVSTAT?= ${DESTDIR}${LIBDIR}/libdevstat.a
|
||||||
LIBDIALOG?= ${DESTDIR}${LIBDIR}/libdialog.a
|
LIBDIALOG?= ${DESTDIR}${LIBDIR}/libdialog.a
|
||||||
|
@ -16,6 +16,7 @@ _PRIVATELIBS= \
|
|||||||
atf_c \
|
atf_c \
|
||||||
atf_cxx \
|
atf_cxx \
|
||||||
bsdstat \
|
bsdstat \
|
||||||
|
devdctl \
|
||||||
event \
|
event \
|
||||||
heimipcc \
|
heimipcc \
|
||||||
heimipcs \
|
heimipcs \
|
||||||
@ -82,6 +83,7 @@ _LIBRARIES= \
|
|||||||
cuse \
|
cuse \
|
||||||
cxxrt \
|
cxxrt \
|
||||||
devctl \
|
devctl \
|
||||||
|
devdctl \
|
||||||
devinfo \
|
devinfo \
|
||||||
devstat \
|
devstat \
|
||||||
dialog \
|
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_leaf)
|
||||||
vd->vdev_ops->vdev_op_close(vd);
|
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 &&
|
if (vd->vdev_removed &&
|
||||||
state == VDEV_STATE_CANT_OPEN &&
|
state == VDEV_STATE_CANT_OPEN &&
|
||||||
(aux == VDEV_AUX_OPEN_FAILED || vd->vdev_checkremove)) {
|
(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;
|
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)
|
if (!isopen && vd->vdev_parent)
|
||||||
vdev_propagate_state(vd->vdev_parent);
|
vdev_propagate_state(vd->vdev_parent);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user