diff options
Diffstat (limited to 'lib/libdevdctl/event.cc')
-rw-r--r-- | lib/libdevdctl/event.cc | 607 |
1 files changed, 607 insertions, 0 deletions
diff --git a/lib/libdevdctl/event.cc b/lib/libdevdctl/event.cc new file mode 100644 index 000000000000..648c7205b121 --- /dev/null +++ b/lib/libdevdctl/event.cc @@ -0,0 +1,607 @@ +/*- + * 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" +/*================================== 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 +{ + char buf[SPECNAMELEN + 1]; + 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. */ + if (fdevname_r(devFd, buf, sizeof(buf)) == NULL) { + close(devFd); + return (false); + } + devName = buf; + 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 +{ + if (Value("subsystem") == "disk") + name = Value("devname"); + else + name = Value("cdev"); + 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 |