]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libdevdctl/event.cc
amd64: use register macros for gdb_cpu_getreg()
[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         string devName;
281
282         if (!DevName(devName))
283                 return (false);
284
285         string devPath(_PATH_DEV + devName);
286         int devFd(open(devPath.c_str(), O_RDONLY));
287         if (devFd == -1)
288                 return (false);
289
290         /* Normalize the device name in case the DEVFS event is for a link. */
291         devName = fdevname(devFd);
292         path = _PATH_DEV + devName;
293
294         close(devFd);
295
296         return (true);
297 }
298
299 bool
300 Event::PhysicalPath(std::string &path) const
301 {
302         string devPath;
303
304         if (!DevPath(devPath))
305                 return (false);
306
307         int devFd(open(devPath.c_str(), O_RDONLY));
308         if (devFd == -1)
309                 return (false);
310         
311         char physPath[MAXPATHLEN];
312         physPath[0] = '\0';
313         bool result(ioctl(devFd, DIOCGPHYSPATH, physPath) == 0);
314         close(devFd);
315         if (result)
316                 path = physPath;
317         return (result);
318 }
319
320 //- Event Protected Methods ----------------------------------------------------
321 Event::Event(Type type, NVPairMap &map, const string &eventString)
322  : m_type(type),
323    m_nvPairs(map),
324    m_eventString(eventString)
325 {
326 }
327
328 Event::Event(const Event &src)
329  : m_type(src.m_type),
330    m_nvPairs(*new NVPairMap(src.m_nvPairs)),
331    m_eventString(src.m_eventString)
332 {
333 }
334
335 void
336 Event::ParseEventString(Event::Type type,
337                               const string &eventString,
338                               NVPairMap& nvpairs)
339 {
340         size_t start;
341         size_t end;
342
343         switch (type) {
344         case ATTACH:
345         case DETACH:
346
347                 /*
348                  * <type><device-name><unit> <pnpvars> \
349                  *                        at <location vars> <pnpvars> \
350                  *                        on <parent>
351                  *
352                  * Handle all data that doesn't conform to the
353                  * "name=value" format, and let the generic parser
354                  * below handle the rest.
355                  *
356                  * Type is a single char.  Skip it.
357                  */
358                 start = 1;
359                 end = eventString.find_first_of(" \t\n", start);
360                 if (end == string::npos)
361                         throw ParseException(ParseException::INVALID_FORMAT,
362                                              eventString, start);
363
364                 nvpairs["device-name"] = eventString.substr(start, end - start);
365
366                 start = eventString.find(" on ", end);
367                 if (end == string::npos)
368                         throw ParseException(ParseException::INVALID_FORMAT,
369                                              eventString, start);
370                 start += 4;
371                 end = eventString.find_first_of(" \t\n", start);
372                 nvpairs["parent"] = eventString.substr(start, end);
373                 break;
374         case NOTIFY:
375                 break;
376         case NOMATCH:
377                 throw ParseException(ParseException::DISCARDED_EVENT_TYPE,
378                                      eventString);
379         default:
380                 throw ParseException(ParseException::UNKNOWN_EVENT_TYPE,
381                                      eventString);
382         }
383
384         /* Process common "key=value" format. */
385         for (start = 1; start < eventString.length(); start = end + 1) {
386
387                 /* Find the '=' in the middle of the key/value pair. */
388                 end = eventString.find('=', start);
389                 if (end == string::npos)
390                         break;
391
392                 /*
393                  * Find the start of the key by backing up until
394                  * we hit whitespace or '!' (event type "notice").
395                  * Due to the devdctl format, all key/value pair must
396                  * start with one of these two characters.
397                  */
398                 start = eventString.find_last_of("! \t\n", end);
399                 if (start == string::npos)
400                         throw ParseException(ParseException::INVALID_FORMAT,
401                                              eventString, end);
402                 start++;
403                 string key(eventString.substr(start, end - start));
404
405                 /*
406                  * Walk forward from the '=' until either we exhaust
407                  * the buffer or we hit whitespace.
408                  */
409                 start = end + 1;
410                 if (start >= eventString.length())
411                         throw ParseException(ParseException::INVALID_FORMAT,
412                                              eventString, end);
413                 end = eventString.find_first_of(" \t\n", start);
414                 if (end == string::npos)
415                         end = eventString.length() - 1;
416                 string value(eventString.substr(start, end - start));
417
418                 nvpairs[key] = value;
419         }
420 }
421
422 void
423 Event::TimestampEventString(std::string &eventString)
424 {
425         if (eventString.size() > 0) {
426                 /*
427                  * Add a timestamp as the final field of the event if it is
428                  * not already present.
429                  */
430                 if (eventString.find(" timestamp=") == string::npos) {
431                         const size_t bufsize = 32;      // Long enough for a 64-bit int
432                         timeval now;
433                         char timebuf[bufsize];
434
435                         size_t eventEnd(eventString.find_last_not_of('\n') + 1);
436                         if (gettimeofday(&now, NULL) != 0)
437                                 err(1, "gettimeofday");
438                         snprintf(timebuf, bufsize, " timestamp=%" PRId64,
439                                 (int64_t) now.tv_sec);
440                         eventString.insert(eventEnd, timebuf);
441                 }
442         }
443 }
444
445 /*-------------------------------- DevfsEvent --------------------------------*/
446 //- DevfsEvent Static Public Methods -------------------------------------------
447 Event *
448 DevfsEvent::Builder(Event::Type type, NVPairMap &nvPairs,
449                     const string &eventString)
450 {
451         return (new DevfsEvent(type, nvPairs, eventString));
452 }
453
454 //- DevfsEvent Static Protected Methods ----------------------------------------
455 bool
456 DevfsEvent::IsWholeDev(const string &devName)
457 {
458         string::const_iterator i(devName.begin());
459
460         size_t start = devName.rfind('/');
461         if (start == string::npos) {
462                 start = 0;
463         } else {
464                 /* Just after the last '/'. */
465                 start++;
466         }
467         i += start;
468
469         /* alpha prefix followed only by digits. */
470         for (; i < devName.end() && !isdigit(*i); i++)
471                 ;
472
473         if (i == devName.end())
474                 return (false);
475
476         for (; i < devName.end() && isdigit(*i); i++)
477                 ;
478
479         return (i == devName.end());
480 }
481
482 //- DevfsEvent Virtual Public Methods ------------------------------------------
483 Event *
484 DevfsEvent::DeepCopy() const
485 {
486         return (new DevfsEvent(*this));
487 }
488
489 bool
490 DevfsEvent::Process() const
491 {
492         return (true);
493 }
494
495 //- DevfsEvent Public Methods --------------------------------------------------
496 bool
497 DevfsEvent::IsWholeDev() const
498 {
499         string devName;
500
501         return (DevName(devName) && IsDiskDev() && IsWholeDev(devName));
502 }
503
504 bool
505 DevfsEvent::DevName(std::string &name) const
506 {
507         if (Value("subsystem") != "CDEV")
508                 return (false);
509
510         name = Value("cdev");
511         return (!name.empty());
512 }
513
514 //- DevfsEvent Protected Methods -----------------------------------------------
515 DevfsEvent::DevfsEvent(Event::Type type, NVPairMap &nvpairs,
516                        const string &eventString)
517  : Event(type, nvpairs, eventString)
518 {
519 }
520
521 DevfsEvent::DevfsEvent(const DevfsEvent &src)
522  : Event(src)
523 {
524 }
525
526 /*--------------------------------- GeomEvent --------------------------------*/
527 //- GeomEvent Static Public Methods --------------------------------------------
528 Event *
529 GeomEvent::Builder(Event::Type type, NVPairMap &nvpairs,
530                    const string &eventString)
531 {
532         return (new GeomEvent(type, nvpairs, eventString));
533 }
534
535 //- GeomEvent Virtual Public Methods -------------------------------------------
536 Event *
537 GeomEvent::DeepCopy() const
538 {
539         return (new GeomEvent(*this));
540 }
541
542 bool
543 GeomEvent::DevName(std::string &name) const
544 {
545         if (Value("subsystem") == "disk")
546                 name = Value("devname");
547         else
548                 name = Value("cdev");
549         return (!name.empty());
550 }
551
552
553 //- GeomEvent Protected Methods ------------------------------------------------
554 GeomEvent::GeomEvent(Event::Type type, NVPairMap &nvpairs,
555                      const string &eventString)
556  : Event(type, nvpairs, eventString),
557    m_devname(Value("devname"))
558 {
559 }
560
561 GeomEvent::GeomEvent(const GeomEvent &src)
562  : Event(src),
563    m_devname(src.m_devname)
564 {
565 }
566
567 /*--------------------------------- ZfsEvent ---------------------------------*/
568 //- ZfsEvent Static Public Methods ---------------------------------------------
569 Event *
570 ZfsEvent::Builder(Event::Type type, NVPairMap &nvpairs,
571                   const string &eventString)
572 {
573         return (new ZfsEvent(type, nvpairs, eventString));
574 }
575
576 //- ZfsEvent Virtual Public Methods --------------------------------------------
577 Event *
578 ZfsEvent::DeepCopy() const
579 {
580         return (new ZfsEvent(*this));
581 }
582
583 bool
584 ZfsEvent::DevName(std::string &name) const
585 {
586         return (false);
587 }
588
589 //- ZfsEvent Protected Methods -------------------------------------------------
590 ZfsEvent::ZfsEvent(Event::Type type, NVPairMap &nvpairs,
591                    const string &eventString)
592  : Event(type, nvpairs, eventString),
593    m_poolGUID(Guid(Value("pool_guid"))),
594    m_vdevGUID(Guid(Value("vdev_guid")))
595 {
596 }
597
598 ZfsEvent::ZfsEvent(const ZfsEvent &src)
599  : Event(src),
600    m_poolGUID(src.m_poolGUID),
601    m_vdevGUID(src.m_vdevGUID)
602 {
603 }
604
605 } // namespace DevdCtl