2 * Copyright (c) 2011, 2012, 2013, 2016 Spectra Logic Corporation
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions, and the following disclaimer,
10 * without modification.
11 * 2. Redistributions in binary form must reproduce at minimum a disclaimer
12 * substantially similar to the "NO WARRANTY" disclaimer below
13 * ("Disclaimer") and any redistribution must be conditioned upon
14 * including a substantially similar Disclaimer requirement for further
15 * binary redistribution.
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
27 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGES.
30 * Authors: Justin T. Gibbs (Spectra Logic Corporation)
36 * Implementation of the class hierarchy used to express events
37 * received via the devdctl API.
39 #include <sys/cdefs.h>
41 #include <sys/filio.h>
42 #include <sys/param.h>
63 #include "event_factory.h"
64 #include "exception.h"
66 __FBSDID("$FreeBSD$");
68 /*================================== Macros ==================================*/
69 #define NUM_ELEMENTS(x) (sizeof(x) / sizeof(*x))
71 /*============================ Namespace Control =============================*/
75 using std::stringstream;
80 /*=========================== Class Implementations ==========================*/
81 /*----------------------------------- Event ----------------------------------*/
82 //- Event Static Protected Data ------------------------------------------------
83 const string Event::s_theEmptyString;
85 Event::EventTypeRecord Event::s_typeTable[] =
87 { Event::NOTIFY, "Notify" },
88 { Event::NOMATCH, "No Driver Match" },
89 { Event::ATTACH, "Attach" },
90 { Event::DETACH, "Detach" }
93 //- Event Static Public Methods ------------------------------------------------
95 Event::Builder(Event::Type type, NVPairMap &nvPairs,
96 const string &eventString)
98 return (new Event(type, nvPairs, eventString));
102 Event::CreateEvent(const EventFactory &factory, const string &eventString)
104 NVPairMap &nvpairs(*new NVPairMap);
105 Type type(static_cast<Event::Type>(eventString[0]));
108 ParseEventString(type, eventString, nvpairs);
109 } catch (const ParseException &exp) {
110 if (exp.GetType() == ParseException::INVALID_FORMAT)
116 * Allow entries in our table for events with no system specified.
117 * These entries should specify the string "none".
119 NVPairMap::iterator system_item(nvpairs.find("system"));
120 if (system_item == nvpairs.end())
121 nvpairs["system"] = "none";
123 return (factory.Build(type, nvpairs, eventString));
127 Event::DevName(std::string &name) const
132 /* TODO: simplify this function with C++-11 <regex> methods */
134 Event::IsDiskDev() const
136 const int numDrivers = 2;
137 static const char *diskDevNames[numDrivers] =
145 if (! DevName(devName))
148 size_t find_start = devName.rfind('/');
149 if (find_start == string::npos) {
152 /* Just after the last '/'. */
156 for (dName = &diskDevNames[0];
157 dName <= &diskDevNames[numDrivers - 1]; dName++) {
159 size_t loc(devName.find(*dName, find_start));
160 if (loc == find_start) {
161 size_t prefixLen(strlen(*dName));
163 if (devName.length() - find_start >= prefixLen
164 && isdigit(devName[find_start + prefixLen]))
173 Event::TypeToString(Event::Type type)
175 EventTypeRecord *rec(s_typeTable);
176 EventTypeRecord *lastRec(s_typeTable + NUM_ELEMENTS(s_typeTable) - 1);
178 for (; rec <= lastRec; rec++) {
179 if (rec->m_type == type)
180 return (rec->m_typeName);
185 //- Event Public Methods -------------------------------------------------------
187 Event::Value(const string &varName) const
189 NVPairMap::const_iterator item(m_nvPairs.find(varName));
190 if (item == m_nvPairs.end())
191 return (s_theEmptyString);
193 return (item->second);
197 Event::Contains(const string &varName) const
199 return (m_nvPairs.find(varName) != m_nvPairs.end());
203 Event::ToString() const
207 NVPairMap::const_iterator devName(m_nvPairs.find("device-name"));
208 if (devName != m_nvPairs.end())
209 result << devName->second << ": ";
211 NVPairMap::const_iterator systemName(m_nvPairs.find("system"));
212 if (systemName != m_nvPairs.end()
213 && systemName->second != "none")
214 result << systemName->second << ": ";
216 result << TypeToString(GetType()) << ' ';
218 for (NVPairMap::const_iterator curVar = m_nvPairs.begin();
219 curVar != m_nvPairs.end(); curVar++) {
220 if (curVar == devName || curVar == systemName)
224 << curVar->first << "=" << curVar->second;
228 return (result.str());
234 cout << ToString() << std::flush;
238 Event::Log(int priority) const
240 syslog(priority, "%s", ToString().c_str());
243 //- Event Virtual Public Methods -----------------------------------------------
250 Event::DeepCopy() const
252 return (new Event(*this));
256 Event::Process() const
262 Event::GetTimestamp() const
264 timeval tv_timestamp;
265 struct tm tm_timestamp;
267 if (!Contains("timestamp")) {
268 throw Exception("Event contains no timestamp: %s",
269 m_eventString.c_str());
271 strptime(Value(string("timestamp")).c_str(), "%s", &tm_timestamp);
272 tv_timestamp.tv_sec = mktime(&tm_timestamp);
273 tv_timestamp.tv_usec = 0;
274 return (tv_timestamp);
278 Event::DevPath(std::string &path) const
280 char buf[SPECNAMELEN + 1];
283 if (!DevName(devName))
286 string devPath(_PATH_DEV + devName);
287 int devFd(open(devPath.c_str(), O_RDONLY));
291 /* Normalize the device name in case the DEVFS event is for a link. */
292 if (fdevname_r(devFd, buf, sizeof(buf)) == NULL) {
297 path = _PATH_DEV + devName;
305 Event::PhysicalPath(std::string &path) const
309 if (!DevPath(devPath))
312 int devFd(open(devPath.c_str(), O_RDONLY));
316 char physPath[MAXPATHLEN];
318 bool result(ioctl(devFd, DIOCGPHYSPATH, physPath) == 0);
325 //- Event Protected Methods ----------------------------------------------------
326 Event::Event(Type type, NVPairMap &map, const string &eventString)
329 m_eventString(eventString)
333 Event::Event(const Event &src)
334 : m_type(src.m_type),
335 m_nvPairs(*new NVPairMap(src.m_nvPairs)),
336 m_eventString(src.m_eventString)
341 Event::ParseEventString(Event::Type type,
342 const string &eventString,
353 * <type><device-name><unit> <pnpvars> \
354 * at <location vars> <pnpvars> \
357 * Handle all data that doesn't conform to the
358 * "name=value" format, and let the generic parser
359 * below handle the rest.
361 * Type is a single char. Skip it.
364 end = eventString.find_first_of(" \t\n", start);
365 if (end == string::npos)
366 throw ParseException(ParseException::INVALID_FORMAT,
369 nvpairs["device-name"] = eventString.substr(start, end - start);
371 start = eventString.find(" on ", end);
372 if (end == string::npos)
373 throw ParseException(ParseException::INVALID_FORMAT,
376 end = eventString.find_first_of(" \t\n", start);
377 nvpairs["parent"] = eventString.substr(start, end);
382 throw ParseException(ParseException::DISCARDED_EVENT_TYPE,
385 throw ParseException(ParseException::UNKNOWN_EVENT_TYPE,
389 /* Process common "key=value" format. */
390 for (start = 1; start < eventString.length(); start = end + 1) {
392 /* Find the '=' in the middle of the key/value pair. */
393 end = eventString.find('=', start);
394 if (end == string::npos)
398 * Find the start of the key by backing up until
399 * we hit whitespace or '!' (event type "notice").
400 * Due to the devdctl format, all key/value pair must
401 * start with one of these two characters.
403 start = eventString.find_last_of("! \t\n", end);
404 if (start == string::npos)
405 throw ParseException(ParseException::INVALID_FORMAT,
408 string key(eventString.substr(start, end - start));
411 * Walk forward from the '=' until either we exhaust
412 * the buffer or we hit whitespace.
415 if (start >= eventString.length())
416 throw ParseException(ParseException::INVALID_FORMAT,
418 end = eventString.find_first_of(" \t\n", start);
419 if (end == string::npos)
420 end = eventString.length() - 1;
421 string value(eventString.substr(start, end - start));
423 nvpairs[key] = value;
428 Event::TimestampEventString(std::string &eventString)
430 if (eventString.size() > 0) {
432 * Add a timestamp as the final field of the event if it is
433 * not already present.
435 if (eventString.find(" timestamp=") == string::npos) {
436 const size_t bufsize = 32; // Long enough for a 64-bit int
438 char timebuf[bufsize];
440 size_t eventEnd(eventString.find_last_not_of('\n') + 1);
441 if (gettimeofday(&now, NULL) != 0)
442 err(1, "gettimeofday");
443 snprintf(timebuf, bufsize, " timestamp=%" PRId64,
444 (int64_t) now.tv_sec);
445 eventString.insert(eventEnd, timebuf);
450 /*-------------------------------- DevfsEvent --------------------------------*/
451 //- DevfsEvent Static Public Methods -------------------------------------------
453 DevfsEvent::Builder(Event::Type type, NVPairMap &nvPairs,
454 const string &eventString)
456 return (new DevfsEvent(type, nvPairs, eventString));
459 //- DevfsEvent Static Protected Methods ----------------------------------------
461 DevfsEvent::IsWholeDev(const string &devName)
463 string::const_iterator i(devName.begin());
465 size_t start = devName.rfind('/');
466 if (start == string::npos) {
469 /* Just after the last '/'. */
474 /* alpha prefix followed only by digits. */
475 for (; i < devName.end() && !isdigit(*i); i++)
478 if (i == devName.end())
481 for (; i < devName.end() && isdigit(*i); i++)
484 return (i == devName.end());
487 //- DevfsEvent Virtual Public Methods ------------------------------------------
489 DevfsEvent::DeepCopy() const
491 return (new DevfsEvent(*this));
495 DevfsEvent::Process() const
500 //- DevfsEvent Public Methods --------------------------------------------------
502 DevfsEvent::IsWholeDev() const
506 return (DevName(devName) && IsDiskDev() && IsWholeDev(devName));
510 DevfsEvent::DevName(std::string &name) const
512 if (Value("subsystem") != "CDEV")
515 name = Value("cdev");
516 return (!name.empty());
519 //- DevfsEvent Protected Methods -----------------------------------------------
520 DevfsEvent::DevfsEvent(Event::Type type, NVPairMap &nvpairs,
521 const string &eventString)
522 : Event(type, nvpairs, eventString)
526 DevfsEvent::DevfsEvent(const DevfsEvent &src)
531 /*--------------------------------- GeomEvent --------------------------------*/
532 //- GeomEvent Static Public Methods --------------------------------------------
534 GeomEvent::Builder(Event::Type type, NVPairMap &nvpairs,
535 const string &eventString)
537 return (new GeomEvent(type, nvpairs, eventString));
540 //- GeomEvent Virtual Public Methods -------------------------------------------
542 GeomEvent::DeepCopy() const
544 return (new GeomEvent(*this));
548 GeomEvent::DevName(std::string &name) const
550 if (Value("subsystem") == "disk")
551 name = Value("devname");
553 name = Value("cdev");
554 return (!name.empty());
558 //- GeomEvent Protected Methods ------------------------------------------------
559 GeomEvent::GeomEvent(Event::Type type, NVPairMap &nvpairs,
560 const string &eventString)
561 : Event(type, nvpairs, eventString),
562 m_devname(Value("devname"))
566 GeomEvent::GeomEvent(const GeomEvent &src)
568 m_devname(src.m_devname)
572 /*--------------------------------- ZfsEvent ---------------------------------*/
573 //- ZfsEvent Static Public Methods ---------------------------------------------
575 ZfsEvent::Builder(Event::Type type, NVPairMap &nvpairs,
576 const string &eventString)
578 return (new ZfsEvent(type, nvpairs, eventString));
581 //- ZfsEvent Virtual Public Methods --------------------------------------------
583 ZfsEvent::DeepCopy() const
585 return (new ZfsEvent(*this));
589 ZfsEvent::DevName(std::string &name) const
594 //- ZfsEvent Protected Methods -------------------------------------------------
595 ZfsEvent::ZfsEvent(Event::Type type, NVPairMap &nvpairs,
596 const string &eventString)
597 : Event(type, nvpairs, eventString),
598 m_poolGUID(Guid(Value("pool_guid"))),
599 m_vdevGUID(Guid(Value("vdev_guid")))
603 ZfsEvent::ZfsEvent(const ZfsEvent &src)
605 m_poolGUID(src.m_poolGUID),
606 m_vdevGUID(src.m_vdevGUID)
610 } // namespace DevdCtl