#include <sys/cdefs.h>
#include <sys/byteorder.h>
#include <sys/time.h>
#include <sys/fs/zfs.h>
#include <sys/vdev_impl.h>
#include <syslog.h>
#include <libzfs.h>
#include <libzutil.h>
#undef flush
#undef __init
#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"
using DevdCtl::Event;
using DevdCtl::Guid;
using DevdCtl::NVPairMap;
using std::stringstream;
Event *
GeomEvent::Builder(Event::Type type,
NVPairMap &nvPairs,
const string &eventString)
{
return (new GeomEvent(type, nvPairs, eventString));
}
Event *
GeomEvent::DeepCopy() const
{
return (new GeomEvent(*this));
}
bool
GeomEvent::Process() const
{
if (CaseFile::Empty())
return (false);
if (Value("type") != "GEOM::physpath" && Value("type") != "CREATE")
return (false);
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) {
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, NULL);
}
}
return (false);
}
GeomEvent::GeomEvent(Event::Type type, NVPairMap &nvpairs,
const string &eventString)
: DevdCtl::GeomEvent(type, nvpairs, eventString)
{
}
GeomEvent::GeomEvent(const GeomEvent &src)
: DevdCtl::GeomEvent::GeomEvent(src)
{
}
nvlist_t *
GeomEvent::ReadLabel(int devFd, bool &inUse, bool °raded)
{
pool_state_t poolState;
char *poolName;
boolean_t b_inuse;
int nlabels;
inUse = false;
degraded = false;
poolName = NULL;
if (zpool_in_use(g_zfsHandle, devFd, &poolState,
&poolName, &b_inuse) == 0) {
nvlist_t *devLabel = NULL;
inUse = b_inuse == B_TRUE;
if (poolName != NULL)
free(poolName);
if (zpool_read_label(devFd, &devLabel, &nlabels) != 0)
return (NULL);
if (nlabels != VDEV_LABELS || devLabel == NULL) {
nvlist_free(devLabel);
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("GeomEvent::ReadLabel: "
+ devPath + ": ");
exp.GetString().insert(0, context);
exp.Log();
nvlist_free(devLabel);
}
}
return (NULL);
}
bool
GeomEvent::OnlineByLabel(const string &devPath, const string& physPath,
nvlist_t *devConfig)
{
bool ret = false;
try {
CaseFileList case_list;
syslog(LOG_INFO, "Interrogating VDEV label for %s\n",
devPath.c_str());
Vdev vdev(devConfig);
CaseFile::Find(vdev.PoolGUID(),vdev.GUID(), case_list);
for (CaseFileList::iterator curr = case_list.begin();
curr != case_list.end(); curr++) {
ret |= (*curr)->ReEvaluate(devPath, physPath, &vdev);
}
return (ret);
} catch (ZfsdException &exp) {
string context("GeomEvent::OnlineByLabel: " + devPath + ": ");
exp.GetString().insert(0, context);
exp.Log();
}
return (ret);
}
DevdCtl::Event *
ZfsEvent::Builder(Event::Type type, NVPairMap &nvpairs,
const string &eventString)
{
return (new ZfsEvent(type, nvpairs, eventString));
}
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);
}
if (Value("type").find("sysevent.fs.zfs.config_sync") == 0) {
ZfsDaemon::Get().ReplayUnconsumedEvents(true);
CaseFile::ReEvaluateByGuid(PoolGUID(), *this);
}
if (Value("type").find("sysevent.fs.zfs.") == 0) {
ProcessPoolEvent();
return (false);
}
if (!Contains("pool_guid") || !Contains("vdev_guid")) {
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);
}
Guid poolGUID(PoolGUID());
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);
}
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->VdevState() == VDEV_STATE_OFFLINE) {
return (false);
}
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::ZfsEvent(Event::Type type, NVPairMap &nvpairs,
const string &eventString)
: DevdCtl::ZfsEvent(type, nvpairs, eventString)
{
}
ZfsEvent::ZfsEvent(const ZfsEvent &src)
: DevdCtl::ZfsEvent(src)
{
}
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);
if (Value("type") == "sysevent.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") == "sysevent.fs.zfs.resilver_finish")
{
Log(LOG_INFO);
CleanupSpares();
}
if (Value("type") == "sysevent.fs.zfs.vdev_remove"
&& degradedDevice == false) {
Log(LOG_INFO);
ZfsDaemon::RequestSystemRescan();
}
}
bool
ZfsEvent::TryDetach(Vdev &vdev, void *cbArg)
{
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();
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());
}
}
return (false);
}