summaryrefslogtreecommitdiffstats
path: root/lib/libdevdctl
diff options
context:
space:
mode:
authorasomers <asomers@FreeBSD.org>2016-05-28 17:43:40 +0000
committerasomers <asomers@FreeBSD.org>2016-05-28 17:43:40 +0000
commit442baa51845cf38dbcfdc44bd8493defdaad630a (patch)
treeb276df22690bfddec70e75c8b9f2f7c8df80cbc6 /lib/libdevdctl
parentda1a7cbaba7d56d77911e9c80b3f82d515ef5e78 (diff)
downloadFreeBSD-src-442baa51845cf38dbcfdc44bd8493defdaad630a.zip
FreeBSD-src-442baa51845cf38dbcfdc44bd8493defdaad630a.tar.gz
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
Diffstat (limited to 'lib/libdevdctl')
-rw-r--r--lib/libdevdctl/Makefile21
-rw-r--r--lib/libdevdctl/consumer.cc258
-rw-r--r--lib/libdevdctl/consumer.h186
-rw-r--r--lib/libdevdctl/event.cc602
-rw-r--r--lib/libdevdctl/event.h423
-rw-r--r--lib/libdevdctl/event_factory.cc99
-rw-r--r--lib/libdevdctl/event_factory.h94
-rw-r--r--lib/libdevdctl/exception.cc125
-rw-r--r--lib/libdevdctl/exception.h168
-rw-r--r--lib/libdevdctl/guid.cc82
-rw-r--r--lib/libdevdctl/guid.h143
-rw-r--r--lib/libdevdctl/tests/Makefile21
-rw-r--r--lib/libdevdctl/tests/libdevdctl_unittest.cc138
13 files changed, 2360 insertions, 0 deletions
diff --git a/lib/libdevdctl/Makefile b/lib/libdevdctl/Makefile
new file mode 100644
index 0000000..5a18a14
--- /dev/null
+++ b/lib/libdevdctl/Makefile
@@ -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>
diff --git a/lib/libdevdctl/consumer.cc b/lib/libdevdctl/consumer.cc
new file mode 100644
index 0000000..6b9822e
--- /dev/null
+++ b/lib/libdevdctl/consumer.cc
@@ -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
diff --git a/lib/libdevdctl/consumer.h b/lib/libdevdctl/consumer.h
new file mode 100644
index 0000000..721461e
--- /dev/null
+++ b/lib/libdevdctl/consumer.h
@@ -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_ */
diff --git a/lib/libdevdctl/event.cc b/lib/libdevdctl/event.cc
new file mode 100644
index 0000000..3c6304b
--- /dev/null
+++ b/lib/libdevdctl/event.cc
@@ -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
diff --git a/lib/libdevdctl/event.h b/lib/libdevdctl/event.h
new file mode 100644
index 0000000..796029a
--- /dev/null
+++ b/lib/libdevdctl/event.h
@@ -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_ */
diff --git a/lib/libdevdctl/event_factory.cc b/lib/libdevdctl/event_factory.cc
new file mode 100644
index 0000000..3901fd5
--- /dev/null
+++ b/lib/libdevdctl/event_factory.cc
@@ -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
diff --git a/lib/libdevdctl/event_factory.h b/lib/libdevdctl/event_factory.h
new file mode 100644
index 0000000..e23332c
--- /dev/null
+++ b/lib/libdevdctl/event_factory.h
@@ -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_ */
diff --git a/lib/libdevdctl/exception.cc b/lib/libdevdctl/exception.cc
new file mode 100644
index 0000000..4dc8cf5
--- /dev/null
+++ b/lib/libdevdctl/exception.cc
@@ -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
diff --git a/lib/libdevdctl/exception.h b/lib/libdevdctl/exception.h
new file mode 100644
index 0000000..9f4403c
--- /dev/null
+++ b/lib/libdevdctl/exception.h
@@ -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_ */
diff --git a/lib/libdevdctl/guid.cc b/lib/libdevdctl/guid.cc
new file mode 100644
index 0000000..42d326f
--- /dev/null
+++ b/lib/libdevdctl/guid.cc
@@ -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
diff --git a/lib/libdevdctl/guid.h b/lib/libdevdctl/guid.h
new file mode 100644
index 0000000..ede414b
--- /dev/null
+++ b/lib/libdevdctl/guid.h
@@ -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_ */
diff --git a/lib/libdevdctl/tests/Makefile b/lib/libdevdctl/tests/Makefile
new file mode 100644
index 0000000..3bbbd0d
--- /dev/null
+++ b/lib/libdevdctl/tests/Makefile
@@ -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>
diff --git a/lib/libdevdctl/tests/libdevdctl_unittest.cc b/lib/libdevdctl/tests/libdevdctl_unittest.cc
new file mode 100644
index 0000000..4f69cb6
--- /dev/null
+++ b/lib/libdevdctl/tests/libdevdctl_unittest.cc
@@ -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"})
+);
OpenPOWER on IntegriCloud