]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libdevdctl/event.cc
Add UPDATING entries and bump version.
[FreeBSD/FreeBSD.git] / lib / libdevdctl / event.cc
1 /*-
2  * Copyright (c) 2011, 2012, 2013, 2016 Spectra Logic Corporation
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
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.
16  *
17  * NO WARRANTY
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.
29  *
30  * Authors: Justin T. Gibbs     (Spectra Logic Corporation)
31  */
32
33 /**
34  * \file event.cc
35  *
36  * Implementation of the class hierarchy used to express events
37  * received via the devdctl API.
38  */
39 #include <sys/cdefs.h>
40 #include <sys/disk.h>
41 #include <sys/filio.h>
42 #include <sys/param.h>
43 #include <sys/stat.h>
44
45 #include <err.h>
46 #include <fcntl.h>
47 #include <inttypes.h>
48 #include <paths.h>
49 #include <stdlib.h>
50 #include <syslog.h>
51 #include <unistd.h>
52
53 #include <cstdarg>
54 #include <cstring>
55 #include <iostream>
56 #include <list>
57 #include <map>
58 #include <sstream>
59 #include <string>
60
61 #include "guid.h"
62 #include "event.h"
63 #include "event_factory.h"
64 #include "exception.h"
65
66 __FBSDID("$FreeBSD$");
67
68 /*================================== Macros ==================================*/
69 #define NUM_ELEMENTS(x) (sizeof(x) / sizeof(*x))
70
71 /*============================ Namespace Control =============================*/
72 using std::cout;
73 using std::endl;
74 using std::string;
75 using std::stringstream;
76
77 namespace DevdCtl
78 {
79
80 /*=========================== Class Implementations ==========================*/
81 /*----------------------------------- Event ----------------------------------*/
82 //- Event Static Protected Data ------------------------------------------------
83 const string Event::s_theEmptyString;
84
85 Event::EventTypeRecord Event::s_typeTable[] =
86 {
87         { Event::NOTIFY,  "Notify" },
88         { Event::NOMATCH, "No Driver Match" },
89         { Event::ATTACH,  "Attach" },
90         { Event::DETACH,  "Detach" }
91 };
92
93 //- Event Static Public Methods ------------------------------------------------
94 Event *
95 Event::Builder(Event::Type type, NVPairMap &nvPairs,
96                const string &eventString)
97 {
98         return (new Event(type, nvPairs, eventString));
99 }
100
101 Event *
102 Event::CreateEvent(const EventFactory &factory, const string &eventString)
103 {
104         NVPairMap &nvpairs(*new NVPairMap);
105         Type       type(static_cast<Event::Type>(eventString[0]));
106
107         try {
108                 ParseEventString(type, eventString, nvpairs);
109         } catch (const ParseException &exp) {
110                 if (exp.GetType() == ParseException::INVALID_FORMAT)
111                         exp.Log();
112                 return (NULL);
113         }
114
115         /*
116          * Allow entries in our table for events with no system specified.
117          * These entries should specify the string "none".
118          */
119         NVPairMap::iterator system_item(nvpairs.find("system"));
120         if (system_item == nvpairs.end())
121                 nvpairs["system"] = "none";
122
123         return (factory.Build(type, nvpairs, eventString));
124 }
125
126 bool
127 Event::DevName(std::string &name) const
128 {
129         return (false);
130 }
131
132 /* TODO: simplify this function with C++-11 <regex> methods */
133 bool
134 Event::IsDiskDev() const
135 {
136         const int numDrivers = 2;
137         static const char *diskDevNames[numDrivers] =
138         {
139                 "da",
140                 "ada"
141         };
142         const char **dName;
143         string devName;
144
145         if (! DevName(devName))
146                 return false;
147
148         size_t find_start = devName.rfind('/');
149         if (find_start == string::npos) {
150                 find_start = 0;
151         } else {
152                 /* Just after the last '/'. */
153                 find_start++;
154         }
155
156         for (dName = &diskDevNames[0];
157              dName <= &diskDevNames[numDrivers - 1]; dName++) {
158
159                 size_t loc(devName.find(*dName, find_start));
160                 if (loc == find_start) {
161                         size_t prefixLen(strlen(*dName));
162
163                         if (devName.length() - find_start >= prefixLen
164                          && isdigit(devName[find_start + prefixLen]))
165                                 return (true);
166                 }
167         }
168
169         return (false);
170 }
171
172 const char *
173 Event::TypeToString(Event::Type type)
174 {
175         EventTypeRecord *rec(s_typeTable);
176         EventTypeRecord *lastRec(s_typeTable + NUM_ELEMENTS(s_typeTable) - 1);
177
178         for (; rec <= lastRec; rec++) {
179                 if (rec->m_type == type)
180                         return (rec->m_typeName);
181         }
182         return ("Unknown");
183 }
184
185 //- Event Public Methods -------------------------------------------------------
186 const string &
187 Event::Value(const string &varName) const
188 {
189         NVPairMap::const_iterator item(m_nvPairs.find(varName));
190         if (item == m_nvPairs.end())
191                 return (s_theEmptyString);
192
193         return (item->second);
194 }
195
196 bool
197 Event::Contains(const string &varName) const
198 {
199         return (m_nvPairs.find(varName) != m_nvPairs.end());
200 }
201
202 string
203 Event::ToString() const
204 {
205         stringstream result;
206
207         NVPairMap::const_iterator devName(m_nvPairs.find("device-name"));
208         if (devName != m_nvPairs.end())
209                 result << devName->second << ": ";
210
211         NVPairMap::const_iterator systemName(m_nvPairs.find("system"));
212         if (systemName != m_nvPairs.end()
213          && systemName->second != "none")
214                 result << systemName->second << ": ";
215
216         result << TypeToString(GetType()) << ' ';
217
218         for (NVPairMap::const_iterator curVar = m_nvPairs.begin();
219              curVar != m_nvPairs.end(); curVar++) {
220                 if (curVar == devName || curVar == systemName)
221                         continue;
222
223                 result << ' '
224                      << curVar->first << "=" << curVar->second;
225         }
226         result << endl;
227
228         return (result.str());
229 }
230
231 void
232 Event::Print() const
233 {
234         cout << ToString() << std::flush;
235 }
236
237 void
238 Event::Log(int priority) const
239 {
240         syslog(priority, "%s", ToString().c_str());
241 }
242
243 //- Event Virtual Public Methods -----------------------------------------------
244 Event::~Event()
245 {
246         delete &m_nvPairs;
247 }
248
249 Event *
250 Event::DeepCopy() const
251 {
252         return (new Event(*this));
253 }
254
255 bool
256 Event::Process() const
257 {
258         return (false);
259 }
260
261 timeval
262 Event::GetTimestamp() const
263 {
264         timeval tv_timestamp;
265         struct tm tm_timestamp;
266
267         if (!Contains("timestamp")) {
268                 throw Exception("Event contains no timestamp: %s",
269                                 m_eventString.c_str());
270         }
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);
275 }
276
277 bool
278 Event::DevPath(std::string &path) const
279 {
280         char buf[SPECNAMELEN + 1];
281         string devName;
282
283         if (!DevName(devName))
284                 return (false);
285
286         string devPath(_PATH_DEV + devName);
287         int devFd(open(devPath.c_str(), O_RDONLY));
288         if (devFd == -1)
289                 return (false);
290
291         /* Normalize the device name in case the DEVFS event is for a link. */
292         if (fdevname_r(devFd, buf, sizeof(buf)) == NULL) {
293                 close(devFd);
294                 return (false);
295         }
296         devName = buf;
297         path = _PATH_DEV + devName;
298
299         close(devFd);
300
301         return (true);
302 }
303
304 bool
305 Event::PhysicalPath(std::string &path) const
306 {
307         string devPath;
308
309         if (!DevPath(devPath))
310                 return (false);
311
312         int devFd(open(devPath.c_str(), O_RDONLY));
313         if (devFd == -1)
314                 return (false);
315         
316         char physPath[MAXPATHLEN];
317         physPath[0] = '\0';
318         bool result(ioctl(devFd, DIOCGPHYSPATH, physPath) == 0);
319         close(devFd);
320         if (result)
321                 path = physPath;
322         return (result);
323 }
324
325 //- Event Protected Methods ----------------------------------------------------
326 Event::Event(Type type, NVPairMap &map, const string &eventString)
327  : m_type(type),
328    m_nvPairs(map),
329    m_eventString(eventString)
330 {
331 }
332
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)
337 {
338 }
339
340 void
341 Event::ParseEventString(Event::Type type,
342                               const string &eventString,
343                               NVPairMap& nvpairs)
344 {
345         size_t start;
346         size_t end;
347
348         switch (type) {
349         case ATTACH:
350         case DETACH:
351
352                 /*
353                  * <type><device-name><unit> <pnpvars> \
354                  *                        at <location vars> <pnpvars> \
355                  *                        on <parent>
356                  *
357                  * Handle all data that doesn't conform to the
358                  * "name=value" format, and let the generic parser
359                  * below handle the rest.
360                  *
361                  * Type is a single char.  Skip it.
362                  */
363                 start = 1;
364                 end = eventString.find_first_of(" \t\n", start);
365                 if (end == string::npos)
366                         throw ParseException(ParseException::INVALID_FORMAT,
367                                              eventString, start);
368
369                 nvpairs["device-name"] = eventString.substr(start, end - start);
370
371                 start = eventString.find(" on ", end);
372                 if (end == string::npos)
373                         throw ParseException(ParseException::INVALID_FORMAT,
374                                              eventString, start);
375                 start += 4;
376                 end = eventString.find_first_of(" \t\n", start);
377                 nvpairs["parent"] = eventString.substr(start, end);
378                 break;
379         case NOTIFY:
380                 break;
381         case NOMATCH:
382                 throw ParseException(ParseException::DISCARDED_EVENT_TYPE,
383                                      eventString);
384         default:
385                 throw ParseException(ParseException::UNKNOWN_EVENT_TYPE,
386                                      eventString);
387         }
388
389         /* Process common "key=value" format. */
390         for (start = 1; start < eventString.length(); start = end + 1) {
391
392                 /* Find the '=' in the middle of the key/value pair. */
393                 end = eventString.find('=', start);
394                 if (end == string::npos)
395                         break;
396
397                 /*
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.
402                  */
403                 start = eventString.find_last_of("! \t\n", end);
404                 if (start == string::npos)
405                         throw ParseException(ParseException::INVALID_FORMAT,
406                                              eventString, end);
407                 start++;
408                 string key(eventString.substr(start, end - start));
409
410                 /*
411                  * Walk forward from the '=' until either we exhaust
412                  * the buffer or we hit whitespace.
413                  */
414                 start = end + 1;
415                 if (start >= eventString.length())
416                         throw ParseException(ParseException::INVALID_FORMAT,
417                                              eventString, end);
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));
422
423                 nvpairs[key] = value;
424         }
425 }
426
427 void
428 Event::TimestampEventString(std::string &eventString)
429 {
430         if (eventString.size() > 0) {
431                 /*
432                  * Add a timestamp as the final field of the event if it is
433                  * not already present.
434                  */
435                 if (eventString.find(" timestamp=") == string::npos) {
436                         const size_t bufsize = 32;      // Long enough for a 64-bit int
437                         timeval now;
438                         char timebuf[bufsize];
439
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);
446                 }
447         }
448 }
449
450 /*-------------------------------- DevfsEvent --------------------------------*/
451 //- DevfsEvent Static Public Methods -------------------------------------------
452 Event *
453 DevfsEvent::Builder(Event::Type type, NVPairMap &nvPairs,
454                     const string &eventString)
455 {
456         return (new DevfsEvent(type, nvPairs, eventString));
457 }
458
459 //- DevfsEvent Static Protected Methods ----------------------------------------
460 bool
461 DevfsEvent::IsWholeDev(const string &devName)
462 {
463         string::const_iterator i(devName.begin());
464
465         size_t start = devName.rfind('/');
466         if (start == string::npos) {
467                 start = 0;
468         } else {
469                 /* Just after the last '/'. */
470                 start++;
471         }
472         i += start;
473
474         /* alpha prefix followed only by digits. */
475         for (; i < devName.end() && !isdigit(*i); i++)
476                 ;
477
478         if (i == devName.end())
479                 return (false);
480
481         for (; i < devName.end() && isdigit(*i); i++)
482                 ;
483
484         return (i == devName.end());
485 }
486
487 //- DevfsEvent Virtual Public Methods ------------------------------------------
488 Event *
489 DevfsEvent::DeepCopy() const
490 {
491         return (new DevfsEvent(*this));
492 }
493
494 bool
495 DevfsEvent::Process() const
496 {
497         return (true);
498 }
499
500 //- DevfsEvent Public Methods --------------------------------------------------
501 bool
502 DevfsEvent::IsWholeDev() const
503 {
504         string devName;
505
506         return (DevName(devName) && IsDiskDev() && IsWholeDev(devName));
507 }
508
509 bool
510 DevfsEvent::DevName(std::string &name) const
511 {
512         if (Value("subsystem") != "CDEV")
513                 return (false);
514
515         name = Value("cdev");
516         return (!name.empty());
517 }
518
519 //- DevfsEvent Protected Methods -----------------------------------------------
520 DevfsEvent::DevfsEvent(Event::Type type, NVPairMap &nvpairs,
521                        const string &eventString)
522  : Event(type, nvpairs, eventString)
523 {
524 }
525
526 DevfsEvent::DevfsEvent(const DevfsEvent &src)
527  : Event(src)
528 {
529 }
530
531 /*--------------------------------- GeomEvent --------------------------------*/
532 //- GeomEvent Static Public Methods --------------------------------------------
533 Event *
534 GeomEvent::Builder(Event::Type type, NVPairMap &nvpairs,
535                    const string &eventString)
536 {
537         return (new GeomEvent(type, nvpairs, eventString));
538 }
539
540 //- GeomEvent Virtual Public Methods -------------------------------------------
541 Event *
542 GeomEvent::DeepCopy() const
543 {
544         return (new GeomEvent(*this));
545 }
546
547 bool
548 GeomEvent::DevName(std::string &name) const
549 {
550         if (Value("subsystem") == "disk")
551                 name = Value("devname");
552         else
553                 name = Value("cdev");
554         return (!name.empty());
555 }
556
557
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"))
563 {
564 }
565
566 GeomEvent::GeomEvent(const GeomEvent &src)
567  : Event(src),
568    m_devname(src.m_devname)
569 {
570 }
571
572 /*--------------------------------- ZfsEvent ---------------------------------*/
573 //- ZfsEvent Static Public Methods ---------------------------------------------
574 Event *
575 ZfsEvent::Builder(Event::Type type, NVPairMap &nvpairs,
576                   const string &eventString)
577 {
578         return (new ZfsEvent(type, nvpairs, eventString));
579 }
580
581 //- ZfsEvent Virtual Public Methods --------------------------------------------
582 Event *
583 ZfsEvent::DeepCopy() const
584 {
585         return (new ZfsEvent(*this));
586 }
587
588 bool
589 ZfsEvent::DevName(std::string &name) const
590 {
591         return (false);
592 }
593
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")))
600 {
601 }
602
603 ZfsEvent::ZfsEvent(const ZfsEvent &src)
604  : Event(src),
605    m_poolGUID(src.m_poolGUID),
606    m_vdevGUID(src.m_vdevGUID)
607 {
608 }
609
610 } // namespace DevdCtl